punchlist 1.2.0 → 1.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +5 -5
  2. data/.circleci/config.yml +132 -0
  3. data/.envrc +4 -0
  4. data/.git-hooks/pre_commit/circle_ci.rb +25 -0
  5. data/.git-hooks/pre_commit/punchlist.rb +51 -0
  6. data/.git-hooks/pre_commit/solargraph_typecheck.rb +64 -0
  7. data/.gitattributes +6 -0
  8. data/.gitignore +60 -0
  9. data/.markdownlint_style.rb +3 -0
  10. data/.mdlrc +1 -0
  11. data/.overcommit.yml +87 -0
  12. data/.pronto.yml +2 -0
  13. data/.rubocop.yml +148 -0
  14. data/.solargraph.yml +26 -0
  15. data/.yamllint.yml +8 -0
  16. data/CODE_OF_CONDUCT.md +133 -0
  17. data/CONTRIBUTING.rst +75 -0
  18. data/ChangeLog.md +8 -0
  19. data/DEVELOPMENT.md +31 -0
  20. data/Gemfile +26 -0
  21. data/Gemfile.lock +186 -0
  22. data/LICENSE +22 -0
  23. data/Makefile +104 -0
  24. data/README.md +27 -0
  25. data/Rakefile +3 -29
  26. data/bin/bump +29 -0
  27. data/bin/overcommit +29 -0
  28. data/bin/punchlist +1 -0
  29. data/bin/rake +29 -0
  30. data/bin/rubocop +27 -0
  31. data/bin/solargraph +27 -0
  32. data/bin/yard +27 -0
  33. data/config/env.1p +0 -0
  34. data/docs/.gitignore +3 -0
  35. data/docs/cookiecutter_input.json +16 -0
  36. data/feature/expected/mixed_types_of_source_files_results.txt +2 -0
  37. data/feature/expected/no_files_results.txt +0 -0
  38. data/feature/expected/non_source_file_with_pis_results.txt +0 -0
  39. data/feature/expected/one_source_file_with_cis_results.txt +1 -0
  40. data/feature/expected/scala_file_to_be_ignored_results.txt +1 -0
  41. data/feature/expected/source_file_with_no_items_results.txt +0 -0
  42. data/feature/feature_helper.rb +41 -0
  43. data/feature/pronto_punchlist_use_spec.rb +26 -0
  44. data/feature/punchlist_cli_spec.rb +63 -0
  45. data/feature/samples/mixed_types_of_source_files/lib/bar.scala +1 -0
  46. data/feature/samples/mixed_types_of_source_files/lib/foo.rb +4 -0
  47. data/feature/samples/no_files/.ignore +0 -0
  48. data/feature/samples/non_source_file_with_pis/foo.doc +5 -0
  49. data/feature/samples/one_source_file_with_cis/app/foo.rb +4 -0
  50. data/feature/samples/scala_file_to_be_ignored/lib/bar.scala +1 -0
  51. data/feature/samples/scala_file_to_be_ignored/lib/foo.rb +4 -0
  52. data/feature/samples/source_file_with_no_items/foo.rb +3 -0
  53. data/fix.sh +411 -0
  54. data/lib/punchlist/config.rb +27 -0
  55. data/lib/punchlist/inspector.rb +51 -0
  56. data/lib/punchlist/offense.rb +23 -0
  57. data/lib/punchlist/{options.rb → option_parser.rb} +9 -14
  58. data/lib/punchlist/renderer.rb +18 -0
  59. data/lib/punchlist/version.rb +3 -1
  60. data/lib/punchlist.rb +12 -45
  61. data/metrics/brakeman_high_water_mark +1 -0
  62. data/metrics/flake8_high_water_mark +1 -0
  63. data/metrics/jscs_high_water_mark +1 -0
  64. data/metrics/punchlist_high_water_mark +1 -0
  65. data/metrics/pycodestyle_high_water_mark +1 -0
  66. data/metrics/rails_best_practices_high_water_mark +1 -0
  67. data/metrics/scalastyle_high_water_mark +1 -0
  68. data/metrics/shellcheck_high_water_mark +1 -0
  69. data/package.json +9 -0
  70. data/punchlist.gemspec +27 -30
  71. data/rakelib/citest.rake +4 -0
  72. data/rakelib/clear_metrics.rake +9 -0
  73. data/rakelib/console.rake +6 -0
  74. data/rakelib/default.rake +4 -0
  75. data/rakelib/doc.rake +6 -0
  76. data/rakelib/feature.rake +10 -0
  77. data/rakelib/gem_tasks.rake +3 -0
  78. data/rakelib/localtest.rake +4 -0
  79. data/rakelib/overcommit.rake +6 -0
  80. data/rakelib/quality.rake +4 -0
  81. data/rakelib/repl.rake +4 -0
  82. data/rakelib/spec.rake +9 -0
  83. data/rakelib/undercover.rake +8 -0
  84. data/requirements_dev.txt +2 -0
  85. metadata +97 -92
  86. data/License.txt +0 -20
data/fix.sh ADDED
@@ -0,0 +1,411 @@
1
+ #!/bin/bash -eu
2
+
3
+ set -o pipefail
4
+
5
+ apt_upgraded=0
6
+
7
+ update_apt() {
8
+ if [ "${apt_upgraded}" = 0 ]
9
+ then
10
+ sudo DEBIAN_FRONTEND=noninteractive apt-get update -y
11
+ apt_upgraded=1
12
+ fi
13
+ }
14
+
15
+ install_rbenv() {
16
+ if [ "$(uname)" == "Darwin" ]
17
+ then
18
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install rbenv || true
19
+ if ! type rbenv 2>/dev/null
20
+ then
21
+ # https://github.com/pyenv/pyenv-installer/blob/master/bin/pyenv-installer
22
+ >&2 cat <<EOF
23
+ WARNING: seems you still have not added 'rbenv' to the load path.
24
+
25
+ # Load rbenv automatically by adding
26
+ # the following to ~/.bashrc:
27
+
28
+ export PATH="$HOME/.rbenv/bin:$PATH"
29
+ eval "$(rbenv init -)"
30
+ EOF
31
+ fi
32
+ else
33
+ git clone https://github.com/rbenv/rbenv.git ~/.rbenv
34
+ fi
35
+ }
36
+
37
+ set_rbenv_env_variables() {
38
+ export PATH="${HOME}/.rbenv/bin:$PATH"
39
+ eval "$(rbenv init -)"
40
+ }
41
+
42
+ install_ruby_build() {
43
+ if [ "$(uname)" == "Darwin" ]
44
+ then
45
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install ruby-build || true
46
+ else
47
+ mkdir -p "$(rbenv root)"/plugins
48
+ git clone https://github.com/rbenv/ruby-build.git "$(rbenv root)"/plugins/ruby-build
49
+ fi
50
+ }
51
+
52
+ ensure_ruby_build() {
53
+ if ! type ruby-build >/dev/null 2>&1 && ! [ -d "${HOME}/.rbenv/plugins/ruby-build" ]
54
+ then
55
+ install_ruby_build
56
+ fi
57
+ }
58
+
59
+ ensure_rbenv() {
60
+ if ! type rbenv >/dev/null 2>&1 && ! [ -f "${HOME}/.rbenv/bin/rbenv" ]
61
+ then
62
+ install_rbenv
63
+ fi
64
+
65
+ set_rbenv_env_variables
66
+
67
+ ensure_ruby_build
68
+ }
69
+
70
+ latest_ruby_version() {
71
+ major_minor=${1}
72
+
73
+ # Double check that this command doesn't error out under -e
74
+ rbenv install --list >/dev/null 2>&1
75
+
76
+ # not sure why, but 'rbenv install --list' below exits with error code
77
+ # 1...after providing the same output the previous line gave when it
78
+ # exited with error code 0.
79
+ #
80
+ # https://github.com/rbenv/rbenv/issues/1441
81
+ set +e
82
+ rbenv install --list 2>/dev/null | cat | grep "^${major_minor}."
83
+ set -e
84
+ }
85
+
86
+ ensure_dev_library() {
87
+ header_file_name=${1:?header file name}
88
+ homebrew_package=${2:?homebrew package}
89
+ apt_package=${3:-${homebrew_package}}
90
+ if ! [ -f /usr/include/"${header_file_name}" ] && \
91
+ ! [ -f /usr/include/x86_64-linux-gnu/"${header_file_name}" ] && \
92
+ ! [ -f /usr/local/include/"${header_file_name}" ] && \
93
+ ! [ -f /usr/local/opt/"${homebrew_package}"/include/"${header_file_name}" ]
94
+ then
95
+ install_package "${homebrew_package}" "${apt_package}"
96
+ fi
97
+ }
98
+
99
+ ensure_ruby_build_requirements() {
100
+ ensure_dev_library readline/readline.h readline libreadline-dev
101
+ ensure_dev_library zlib.h zlib zlib1g-dev
102
+ ensure_dev_library openssl/ssl.h openssl libssl-dev
103
+ ensure_dev_library yaml.h libyaml libyaml-dev
104
+ }
105
+
106
+ ensure_latest_ruby_build_definitions() {
107
+ ensure_rbenv
108
+
109
+ git -C "$(rbenv root)"/plugins/ruby-build pull
110
+ }
111
+
112
+ # You can find out which feature versions are still supported / have
113
+ # been release here: https://www.ruby-lang.org/en/downloads/
114
+ ensure_ruby_versions() {
115
+ ensure_latest_ruby_build_definitions
116
+
117
+ # You can find out which feature versions are still supported / have
118
+ # been release here: https://www.ruby-lang.org/en/downloads/
119
+ ruby_versions="$(latest_ruby_version 3.1)"
120
+
121
+ echo "Latest Ruby versions: ${ruby_versions}"
122
+
123
+ ensure_ruby_build_requirements
124
+
125
+ for ver in $ruby_versions
126
+ do
127
+ rbenv install -s "${ver}"
128
+ hash -r # ensure we are seeing latest bundler etc
129
+ done
130
+ }
131
+
132
+ ensure_bundle() {
133
+ # Not sure why this is needed a second time, but it seems to be?
134
+ #
135
+ # https://app.circleci.com/pipelines/github/apiology/source_finder/21/workflows/88db659f-a4f4-4751-abc0-46f5929d8e58/jobs/107
136
+ set_rbenv_env_variables
137
+ bundle --version >/dev/null 2>&1 || gem install --no-document bundler
138
+ bundler_version=$(bundle --version | cut -d ' ' -f3)
139
+ bundler_version_major=$(cut -d. -f1 <<< "${bundler_version}")
140
+ bundler_version_minor=$(cut -d. -f2 <<< "${bundler_version}")
141
+ bundler_version_patch=$(cut -d. -f3 <<< "${bundler_version}")
142
+ # Version 2.1 of bundler seems to have some issues with nokogiri:
143
+ #
144
+ # https://app.asana.com/0/1107901397356088/1199504270687298
145
+
146
+ # Version <2.2.22 of bundler isn't compatible with Ruby 3.3:
147
+ #
148
+ # https://stackoverflow.com/questions/70800753/rails-calling-didyoumeanspell-checkers-mergeerror-name-spell-checker-h
149
+ #
150
+ #
151
+ # Version 2.5.5 fixed an issue in 2.2.22 with the 'bump' gem:
152
+ #
153
+ # https://app.circleci.com/pipelines/github/apiology/checkoff/1281/workflows/f667f909-c3fc-4ae2-8593-dde2b588a7a7/jobs/2491
154
+ need_better_bundler=false
155
+ if [ "${bundler_version_major}" -lt 2 ]
156
+ then
157
+ need_better_bundler=true
158
+ elif [ "${bundler_version_major}" -eq 2 ]
159
+ then
160
+ if [ "${bundler_version_minor}" -lt 5 ]
161
+ then
162
+ need_better_bundler=true
163
+ elif [ "${bundler_version_minor}" -eq 5 ]
164
+ then
165
+ if [ "${bundler_version_patch}" -lt 5 ]
166
+ then
167
+ need_better_bundler=true
168
+ fi
169
+ fi
170
+ fi
171
+ if [ "${need_better_bundler}" = true ]
172
+ then
173
+ >&2 echo "Original bundler version: ${bundler_version}"
174
+ # need to do this first before 'bundle update --bundler' will work
175
+ make bundle_install
176
+ bundle update --bundler
177
+ gem install bundler:2.5.5
178
+ >&2 echo "Updated bundler version: $(bundle --version)"
179
+ # ensure next step installs fresh bundle
180
+ rm -f Gemfile.lock.installed
181
+ fi
182
+ make bundle_install
183
+ # https://bundler.io/v2.0/bundle_lock.html#SUPPORTING-OTHER-PLATFORMS
184
+ #
185
+ # "If you want your bundle to support platforms other than the one
186
+ # you're running locally, you can run bundle lock --add-platform
187
+ # PLATFORM to add PLATFORM to the lockfile, force bundler to
188
+ # re-resolve and consider the new platform when picking gems, all
189
+ # without needing to have a machine that matches PLATFORM handy to
190
+ # install those platform-specific gems on.'
191
+ #
192
+ # This affects nokogiri, which will try to reinstall itself in
193
+ # Docker builds where it's already installed if this is not run.
194
+ for platform in arm64-darwin-23 x86_64-darwin-23 x86_64-linux x86_64-linux-musl
195
+ do
196
+ grep "${platform:?}" Gemfile.lock >/dev/null 2>&1 || bundle lock --add-platform "${platform:?}"
197
+ done
198
+ }
199
+
200
+ set_ruby_local_version() {
201
+ latest_ruby_version="$(cut -d' ' -f1 <<< "${ruby_versions}")"
202
+ echo "${latest_ruby_version}" > .ruby-version
203
+ }
204
+
205
+ latest_python_version() {
206
+ major_minor=${1}
207
+ # https://stackoverflow.com/questions/369758/how-to-trim-whitespace-from-a-bash-variable
208
+ pyenv install --list | grep "^ ${major_minor}." | grep -v -- -dev | tail -1 | xargs
209
+ }
210
+
211
+ install_pyenv() {
212
+ if [ "$(uname)" == "Darwin" ]
213
+ then
214
+ HOMEBREW_NO_AUTO_UPDATE=1 brew install pyenv || true
215
+ if ! type pyenv 2>/dev/null
216
+ then
217
+ # https://github.com/pyenv/pyenv-installer/blob/master/bin/pyenv-installer
218
+ >&2 cat <<EOF
219
+ WARNING: seems you still have not added 'pyenv' to the load path.
220
+
221
+ # Load pyenv automatically by adding
222
+ # the following to ~/.bashrc:
223
+
224
+ export PYENV_ROOT="${HOME}/.pyenv"
225
+ export PATH="${PYENV_ROOT}/bin:$PATH"
226
+ eval "$(pyenv init --path)"
227
+ eval "$(pyenv virtualenv-init -)"
228
+ EOF
229
+ fi
230
+ else
231
+ curl https://pyenv.run | bash
232
+ fi
233
+ }
234
+
235
+ set_pyenv_env_variables() {
236
+ # looks like pyenv scripts aren't -u clean:
237
+ #
238
+ # https://app.circleci.com/pipelines/github/apiology/cookiecutter-pypackage/15/workflows/10506069-7662-46bd-b915-2992db3f795b/jobs/15
239
+ set +u
240
+ export PYENV_ROOT="${HOME}/.pyenv"
241
+ export PATH="${PYENV_ROOT}/bin:$PATH"
242
+ eval "$(pyenv init --path)"
243
+ eval "$(pyenv virtualenv-init -)"
244
+ set -u
245
+ }
246
+
247
+ ensure_pyenv() {
248
+ if ! type pyenv >/dev/null 2>&1 && ! [ -f "${HOME}/.pyenv/bin/pyenv" ]
249
+ then
250
+ install_pyenv
251
+ fi
252
+
253
+ if ! type pyenv >/dev/null 2>&1
254
+ then
255
+ set_pyenv_env_variables
256
+ fi
257
+ }
258
+
259
+ install_package() {
260
+ homebrew_package=${1:?homebrew package}
261
+ apt_package=${2:-${homebrew_package}}
262
+ if [ "$(uname)" == "Darwin" ]
263
+ then
264
+ HOMEBREW_NO_AUTO_UPDATE=1 HOMEBREW_NO_INSTALL_UPGRADE=1 brew install "${homebrew_package}"
265
+ elif type apt-get >/dev/null 2>&1
266
+ then
267
+ if ! dpkg -s "${apt_package}" >/dev/null
268
+ then
269
+ update_apt
270
+ sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${apt_package}"
271
+ fi
272
+ else
273
+ >&2 echo "Teach me how to install packages on this plaform"
274
+ exit 1
275
+ fi
276
+ }
277
+
278
+ update_package() {
279
+ homebrew_package=${1:?homebrew package}
280
+ apt_package=${2:-${homebrew_package}}
281
+ if [ "$(uname)" == "Darwin" ]
282
+ then
283
+ brew install "${homebrew_package}"
284
+ elif type apt-get >/dev/null 2>&1
285
+ then
286
+ update_apt
287
+ sudo DEBIAN_FRONTEND=noninteractive apt-get install -y "${apt_package}"
288
+ else
289
+ >&2 echo "Teach me how to install packages on this plaform"
290
+ exit 1
291
+ fi
292
+ }
293
+
294
+ ensure_python_build_requirements() {
295
+ ensure_dev_library zlib.h zlib zlib1g-dev
296
+ ensure_dev_library bzlib.h bzip2 libbz2-dev
297
+ ensure_dev_library openssl/ssl.h openssl libssl-dev
298
+ ensure_dev_library ffi.h libffi libffi-dev
299
+ ensure_dev_library sqlite3.h sqlite3 libsqlite3-dev
300
+ ensure_dev_library lzma.h xz liblzma-dev
301
+ ensure_dev_library readline/readline.h readline libreadline-dev
302
+ }
303
+
304
+ # You can find out which feature versions are still supported / have
305
+ # been release here: https://www.python.org/downloads/
306
+ ensure_python_versions() {
307
+ # You can find out which feature versions are still supported / have
308
+ # been release here: https://www.python.org/downloads/
309
+ python_versions="$(latest_python_version 3.12)"
310
+
311
+ echo "Latest Python versions: ${python_versions}"
312
+
313
+ ensure_python_build_requirements
314
+
315
+ for ver in $python_versions
316
+ do
317
+ if [ "$(uname)" == Darwin ]
318
+ then
319
+ if [ -z "${HOMEBREW_OPENSSL_PREFIX:-}" ]
320
+ then
321
+ HOMEBREW_OPENSSL_PREFIX="$(brew --prefix openssl)"
322
+ fi
323
+ pyenv_install() {
324
+ CFLAGS="-I/usr/local/opt/zlib/include -I/usr/local/opt/bzip2/include -I${HOMEBREW_OPENSSL_PREFIX}/include" LDFLAGS="-L/usr/local/opt/zlib/lib -L/usr/local/opt/bzip2/lib -L${HOMEBREW_OPENSSL_PREFIX}/lib" pyenv install --skip-existing "$@"
325
+ }
326
+
327
+ major_minor="$(cut -d. -f1-2 <<<"${ver}")"
328
+ pyenv_install "${ver}"
329
+ else
330
+ pyenv install -s "${ver}"
331
+ fi
332
+ done
333
+ }
334
+
335
+ ensure_pyenv_virtualenvs() {
336
+ latest_python_version="$(cut -d' ' -f1 <<< "${python_versions}")"
337
+ virtualenv_name="punchlist-${latest_python_version}"
338
+ pyenv virtualenv "${latest_python_version}" "${virtualenv_name}" || true
339
+ # You can use this for your global stuff!
340
+ pyenv virtualenv "${latest_python_version}" mylibs || true
341
+ # shellcheck disable=SC2086
342
+ pyenv local "${virtualenv_name}" ${python_versions} mylibs
343
+ }
344
+
345
+ ensure_pip_and_wheel() {
346
+ # https://cve.mitre.org/cgi-bin/cvename.cgi?name=2023-5752
347
+ major_pip_version=$(pip --version | cut -d' ' -f2 | cut -d '.' -f 1)
348
+ minor_pip_version=$(pip --version | cut -d' ' -f2 | cut -d '.' -f 2)
349
+ if [[ major_pip_version -lt 23 ]]
350
+ then
351
+ pip install 'pip>=23.3'
352
+ elif [[ major_pip_version -eq 23 ]] && [[ minor_pip_version -lt 3 ]]
353
+ then
354
+ pip install 'pip>=23.3'
355
+ fi
356
+ # wheel is helpful for being able to cache long package builds
357
+ pip show wheel >/dev/null 2>&1 || pip install wheel
358
+ }
359
+
360
+ ensure_python_requirements() {
361
+ make pip_install
362
+ }
363
+
364
+ ensure_shellcheck() {
365
+ if ! type shellcheck >/dev/null 2>&1
366
+ then
367
+ install_package shellcheck
368
+ fi
369
+ }
370
+
371
+ ensure_overcommit() {
372
+ # don't run if we're in the middle of a cookiecutter child project
373
+ # test, or otherwise don't have a Git repo to install hooks into...
374
+ if [ -d .git ]
375
+ then
376
+ bundle exec overcommit --install
377
+ bundle exec overcommit --sign pre-commit
378
+ else
379
+ >&2 echo 'Not in a git repo; not installing git hooks'
380
+ fi
381
+ }
382
+
383
+ ensure_rugged_packages_installed() {
384
+ install_package icu4c libicu-dev # needed by rugged, needed by undercover
385
+ install_package pkg-config # needed by rugged, needed by undercover
386
+ install_package cmake # needed by rugged, needed by undercover
387
+ }
388
+
389
+ ensure_rbenv
390
+
391
+ ensure_ruby_versions
392
+
393
+ set_ruby_local_version
394
+
395
+ ensure_rugged_packages_installed
396
+
397
+ ensure_bundle
398
+
399
+ ensure_pyenv
400
+
401
+ ensure_python_versions
402
+
403
+ ensure_pyenv_virtualenvs
404
+
405
+ ensure_pip_and_wheel
406
+
407
+ ensure_python_requirements
408
+
409
+ ensure_shellcheck
410
+
411
+ ensure_overcommit
@@ -0,0 +1,27 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Punchlist
4
+ # Configuration for punchlist gem
5
+ class Config
6
+ attr_reader :regexp, :glob, :exclude
7
+
8
+ def self.default_punchlist_line_regexp_string
9
+ 'XXX|TODO|FIXME|OPTIMIZE|HACK|REVIEW|LATER|FIXIT'
10
+ end
11
+
12
+ def source_files
13
+ @source_file_globber.source_files_glob = glob if glob
14
+ @source_file_globber.source_files_exclude_glob = exclude if exclude
15
+ @source_file_globber.source_files_arr
16
+ end
17
+
18
+ def initialize(regexp: nil, glob: nil, exclude: nil,
19
+ source_file_globber:)
20
+ @regexp = Regexp.new(regexp ||
21
+ Config.default_punchlist_line_regexp_string)
22
+ @glob = glob
23
+ @exclude = exclude
24
+ @source_file_globber = source_file_globber
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'offense'
4
+
5
+ module Punchlist
6
+ # Inspects files for punchlist items
7
+ class Inspector
8
+ # @param punchlist_line_regexp [Regexp] a regular expression that matches punchlist items
9
+ # @param filename [String] the file to inspect
10
+ # @param file_opener [Class<File>] an object that responds to `open` like `File`
11
+ def initialize(punchlist_line_regexp, filename, file_opener: File)
12
+ @file_opener = file_opener
13
+ @punchlist_line_regexp = punchlist_line_regexp
14
+ @filename = filename
15
+ @lines = []
16
+ @line_num = 0
17
+ end
18
+
19
+ # @return [Array<Offense>] punchlist items for the specified file
20
+ def run
21
+ @file_opener.open(filename, 'r') do |file|
22
+ file.each_line { |line| inspect_line(line) }
23
+ end
24
+ @lines
25
+ end
26
+
27
+ private
28
+
29
+ # @return [Regexp]
30
+ attr_reader :punchlist_line_regexp
31
+
32
+ # @return [String]
33
+ attr_reader :filename
34
+
35
+ # Inspects a line for punchlist items and stores it in this objects state
36
+ #
37
+ # @param line [String] the line to inspect
38
+ # @return [void]
39
+ def inspect_line(line)
40
+ @line_num += 1
41
+ return unless line =~ punchlist_line_regexp
42
+
43
+ @lines << Offense.new(filename, @line_num, line.chomp)
44
+ rescue ArgumentError => e
45
+ if e.message != 'invalid byte sequence in UTF-8'
46
+ # not a simple binary file we should ignore
47
+ raise
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Punchlist
4
+ # Represents a discovered punchlist item in code
5
+ class Offense
6
+ attr_reader :filename, :line_num, :line
7
+ def initialize(filename, line_num, line)
8
+ @filename = filename
9
+ @line_num = line_num
10
+ @line = line
11
+ end
12
+
13
+ def ==(other)
14
+ other.class == self.class && other.state == state
15
+ end
16
+
17
+ protected
18
+
19
+ def state
20
+ [@filename, @line_num, @line]
21
+ end
22
+ end
23
+ end
@@ -1,9 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'optparse'
2
4
  require 'source_finder/option_parser'
5
+ require_relative 'config'
3
6
 
4
7
  module Punchlist
5
8
  # Parse command line options
6
- class Options
9
+ class OptionParser
7
10
  attr_reader :default_punchlist_line_regexp
8
11
 
9
12
  def initialize(args,
@@ -12,18 +15,10 @@ module Punchlist
12
15
  @source_finder_option_parser = source_finder_option_parser
13
16
  end
14
17
 
15
- def self.default_punchlist_line_regexp_string
16
- 'XXX|TODO|FIXME|OPTIMIZE|HACK|REVIEW|LATER|FIXIT'
17
- end
18
-
19
- def self.default_punchlist_line_regexp
20
- Regexp.new(default_punchlist_line_regexp_string)
21
- end
22
-
23
18
  def parse_regexp(opts, options)
24
19
  opts.on('-r', '--regexp r',
25
- 'Regexp to trigger opon - default is ' \
26
- "#{self.class.default_punchlist_line_regexp_string}") do |v|
20
+ 'Regexp to trigger upon - default is ' \
21
+ "#{Config.default_punchlist_line_regexp_string}") do |v|
27
22
  options[:regexp] = v
28
23
  end
29
24
  end
@@ -36,12 +31,12 @@ module Punchlist
36
31
  options
37
32
  end
38
33
 
39
- def parse_options
34
+ def generate_config(source_file_globber)
40
35
  options = nil
41
- OptionParser.new do |opts|
36
+ ::OptionParser.new do |opts|
42
37
  options = setup_options(opts)
43
38
  end.parse!(@args)
44
- options
39
+ Config.new(**options, source_file_globber: source_file_globber)
45
40
  end
46
41
  end
47
42
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Punchlist
4
+ # Render a text format of offenses
5
+ class CliRenderer
6
+ def render(output)
7
+ lines = output.map do |offense|
8
+ "#{offense.filename}:#{offense.line_num}: #{offense.line}"
9
+ end
10
+ out = lines.join("\n")
11
+ if out.empty?
12
+ out
13
+ else
14
+ out + "\n"
15
+ end
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,7 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # Counts the number of 'todo' comments in your code ('Comment
2
4
  # annotations').
3
5
  module Punchlist
4
- VERSION = '1.2.0'
6
+ VERSION = '1.3.1'
5
7
  end
data/lib/punchlist.rb CHANGED
@@ -1,26 +1,25 @@
1
- require_relative 'punchlist/options'
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'punchlist/version'
4
+ require_relative 'punchlist/option_parser'
5
+ require_relative 'punchlist/inspector'
6
+ require_relative 'punchlist/renderer'
2
7
 
3
8
  # XXX: need to include BUG in list
4
9
  # XXX: need to include BUG in my rubocop config
5
- # BUG need to fix the fact that we create blank lines on files with no issues
6
10
  module Punchlist
7
11
  # Counts the number of 'todo' comments in your code.
8
12
  class Punchlist
9
13
  def initialize(args,
10
14
  outputter: STDOUT,
11
- file_opener: File,
12
- options_parser: Options.new(args),
15
+ option_parser_class: OptionParser,
13
16
  source_file_globber: SourceFinder::SourceFileGlobber.new)
14
- @args = args
17
+ option_parser = option_parser_class.new(args)
18
+ @config = option_parser.generate_config(source_file_globber)
15
19
  @outputter = outputter
16
- @file_opener = file_opener
17
- @options_parser = options_parser
18
- @source_file_globber = source_file_globber
19
20
  end
20
21
 
21
22
  def run
22
- @options = @options_parser.parse_options
23
-
24
23
  analyze_files
25
24
 
26
25
  0
@@ -28,50 +27,18 @@ module Punchlist
28
27
 
29
28
  def analyze_files
30
29
  all_output = []
31
- source_files.each do |filename|
30
+ @config.source_files.each do |filename|
32
31
  all_output.concat(look_for_punchlist_items(filename))
33
32
  end
34
33
  @outputter.print render(all_output)
35
34
  end
36
35
 
37
- def source_files
38
- if @options[:glob]
39
- @source_file_globber.source_files_glob = @options[:glob]
40
- end
41
- if @options[:exclude]
42
- @source_file_globber.source_files_exclude_glob = @options[:exclude]
43
- end
44
- @source_file_globber.source_files_arr
45
- end
46
-
47
- def punchlist_line_regexp
48
- return @regexp if @regexp
49
-
50
- regexp_string = @options[:regexp]
51
- if regexp_string
52
- @regexp = Regexp.new(regexp_string)
53
- else
54
- Options.default_punchlist_line_regexp
55
- end
56
- end
57
-
58
36
  def look_for_punchlist_items(filename)
59
- lines = []
60
- line_num = 0
61
- @file_opener.open(filename, 'r') do |file|
62
- file.each_line do |line|
63
- line_num += 1
64
- lines << [filename, line_num, line] if line =~ punchlist_line_regexp
65
- end
66
- end
67
- lines
37
+ Inspector.new(@config.regexp, filename).run
68
38
  end
69
39
 
70
40
  def render(output)
71
- lines = output.map do |filename, line_num, line|
72
- "#{filename}:#{line_num}: #{line}"
73
- end
74
- lines.join
41
+ CliRenderer.new.render(output)
75
42
  end
76
43
  end
77
44
  end
@@ -0,0 +1 @@
1
+ 0
@@ -0,0 +1 @@
1
+ 0
@@ -0,0 +1 @@
1
+ 0
@@ -0,0 +1 @@
1
+ 17
@@ -0,0 +1 @@
1
+ 0