summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorymap <ymap.dev@gmail.com>2023-09-13 23:53:24 +0900
committerGitHub <noreply@github.com>2023-09-13 15:53:24 +0100
commit9092af9ad6a5c93a759be571323ea1d0cafa8d6e (patch)
tree6c58290794d91819fb2d80d2da8752997627537b
parentac615e7f656fb379cfe7cd205842b0f089a060d6 (diff)
downloadale-9092af9ad6a5c93a759be571323ea1d0cafa8d6e.zip
Add support for Packwerk (#4594)
Packwerk (https://github.com/Shopify/packwerk) is a Ruby gem used to enforce boundaries and modularize Rails applications.
-rw-r--r--ale_linters/ruby/packwerk.vim55
-rw-r--r--doc/ale-ruby.txt20
-rw-r--r--doc/ale-supported-languages-and-tools.txt1
-rw-r--r--doc/ale.txt1
-rw-r--r--supported-tools.md1
-rw-r--r--test/handler/test_packwerk_handler.vader29
-rw-r--r--test/linter/test_packwerk.vader38
7 files changed, 145 insertions, 0 deletions
diff --git a/ale_linters/ruby/packwerk.vim b/ale_linters/ruby/packwerk.vim
new file mode 100644
index 00000000..4014b2da
--- /dev/null
+++ b/ale_linters/ruby/packwerk.vim
@@ -0,0 +1,55 @@
+" Author: ymap - https://github.com/ymap
+" Description: Packwerk, a static analyzer used to enforce boundaries and modularize Rails applications.
+
+call ale#Set('ruby_packwerk_executable', 'packwerk')
+call ale#Set('ruby_packwerk_options', '')
+
+function! ale_linters#ruby#packwerk#Handle(buffer, lines) abort
+ let l:pattern = '\v^[^:]+:(\d+):(\d+)$'
+ let l:index = 0
+ let l:output = []
+
+ while l:index < len(a:lines) - 1
+ let l:cleaned_line = substitute(a:lines[l:index], '\v\e\[[0-9;]*m', '', 'g')
+ let l:match = matchlist(l:cleaned_line, l:pattern)
+
+ if len(l:match) > 0
+ call add(l:output, {
+ \ 'lnum': l:match[1] + 0,
+ \ 'col': l:match[2] + 0,
+ \ 'text': a:lines[l:index + 1],
+ \})
+ endif
+
+ let l:index += 1
+ endwhile
+
+ return l:output
+endfunction
+
+function! ale_linters#ruby#packwerk#GetCommand(buffer) abort
+ let l:rails_root = ale#ruby#FindRailsRoot(a:buffer)
+
+ if l:rails_root is? ''
+ return ''
+ endif
+
+ let l:executable = ale#Var(a:buffer, 'ruby_packwerk_executable')
+ let l:sep = has('win32') ? '\' : '/'
+ let l:abs_path = expand('#' . a:buffer . ':p')
+ let l:rel_path = substitute(l:abs_path, escape(l:rails_root . l:sep, '\'), '', '')
+
+ return ale#ruby#EscapeExecutable(l:executable, 'packwerk')
+ \ . ' check'
+ \ . ale#Pad(ale#Var(a:buffer, 'ruby_packwerk_options'))
+ \ . ' '
+ \ . ale#Escape(rel_path)
+endfunction
+
+call ale#linter#Define('ruby', {
+\ 'name': 'packwerk',
+\ 'executable': {b -> ale#Var(b, 'ruby_packwerk_executable')},
+\ 'command': function('ale_linters#ruby#packwerk#GetCommand'),
+\ 'callback': 'ale_linters#ruby#packwerk#Handle',
+\ 'lint_file': 1,
+\})
diff --git a/doc/ale-ruby.txt b/doc/ale-ruby.txt
index b0773fdd..ff9aa798 100644
--- a/doc/ale-ruby.txt
+++ b/doc/ale-ruby.txt
@@ -49,6 +49,26 @@ g:ale_ruby_debride_options *g:ale_ruby_debride_options*
===============================================================================
+packwerk *ale-ruby-packwerk*
+
+g:ale_ruby_packwerk_executable *g:ale_ruby_packwerk_executable*
+ *b:ale_ruby_packwerk_executable*
+ Type: |String|
+ Default: `'packwerk'`
+
+ Override the invoked packwerk binary. Set this to `'bundle'` to invoke
+ `'bundle` `exec` packwerk'.
+
+
+g:ale_ruby_packwerk_options *g:ale_ruby_packwerk_options*
+ *b:ale_ruby_packwerk_options*
+ Type: |String|
+ Default: `''`
+
+ This variable can be changed to modify flags given to packwerk.
+
+
+===============================================================================
prettier *ale-ruby-prettier*
See |ale-javascript-prettier| for information about the available options.
diff --git a/doc/ale-supported-languages-and-tools.txt b/doc/ale-supported-languages-and-tools.txt
index ff5a914f..b973b8b4 100644
--- a/doc/ale-supported-languages-and-tools.txt
+++ b/doc/ale-supported-languages-and-tools.txt
@@ -547,6 +547,7 @@ Notes:
* `brakeman`!!
* `cspell`
* `debride`
+ * `packwerk`!!
* `prettier`
* `rails_best_practices`!!
* `reek`
diff --git a/doc/ale.txt b/doc/ale.txt
index ded91aa3..423204a3 100644
--- a/doc/ale.txt
+++ b/doc/ale.txt
@@ -3324,6 +3324,7 @@ documented in additional help files.
brakeman..............................|ale-ruby-brakeman|
cspell................................|ale-ruby-cspell|
debride...............................|ale-ruby-debride|
+ packwerk..............................|ale-ruby-packwerk|
prettier..............................|ale-ruby-prettier|
rails_best_practices..................|ale-ruby-rails_best_practices|
reek..................................|ale-ruby-reek|
diff --git a/supported-tools.md b/supported-tools.md
index 637e771d..05bea334 100644
--- a/supported-tools.md
+++ b/supported-tools.md
@@ -556,6 +556,7 @@ formatting.
* [brakeman](http://brakemanscanner.org/) :floppy_disk:
* [cspell](https://github.com/streetsidesoftware/cspell/tree/main/packages/cspell)
* [debride](https://github.com/seattlerb/debride)
+ * [packwerk](https://github.com/Shopify/packwerk) :floppy_disk:
* [prettier](https://github.com/prettier/plugin-ruby)
* [rails_best_practices](https://github.com/flyerhzm/rails_best_practices) :floppy_disk:
* [reek](https://github.com/troessner/reek)
diff --git a/test/handler/test_packwerk_handler.vader b/test/handler/test_packwerk_handler.vader
new file mode 100644
index 00000000..b95b7556
--- /dev/null
+++ b/test/handler/test_packwerk_handler.vader
@@ -0,0 +1,29 @@
+Before:
+ call ale#test#SetDirectory('/testplugin/test/handler')
+ cd ..
+
+ runtime ale_linters/ruby/packwerk.vim
+
+After:
+ call ale#test#RestoreDirectory()
+ call ale#linter#Reset()
+
+Execute(The packwerk handler should parse lines correctly):
+ call ale#test#SetFilename('test-files/ruby/valid_rails_app/app/models/thing.rb')
+
+ AssertEqual
+ \ [
+ \ {
+ \ 'lnum': 36,
+ \ 'col': 100,
+ \ 'text': 'Dependency violation: ::Edi::Source belongs to ''edi'', but ''billing'' does not specify a dependency on ''edi''.',
+ \ }
+ \ ],
+ \ ale_linters#ruby#packwerk#Handle(bufnr(''), [
+ \ '/Users/JaneDoe/src/github.com/sample-project/billing/app/jobs/document_processing_job.rb:36:100',
+ \ 'Dependency violation: ::Edi::Source belongs to ''edi'', but ''billing'' does not specify a dependency on ''edi''.',
+ \ 'Are we missing an abstraction?',
+ \ 'Is the code making the reference, and the referenced constant, in the right packages?',
+ \ '',
+ \ 'Inference details: ''Edi::Source'' refers to ::Edi::Source which seems to be defined in edi/app/models/edi/source.rb.',
+ \ ])
diff --git a/test/linter/test_packwerk.vader b/test/linter/test_packwerk.vader
new file mode 100644
index 00000000..9a91fd9e
--- /dev/null
+++ b/test/linter/test_packwerk.vader
@@ -0,0 +1,38 @@
+Before:
+ call ale#assert#SetUpLinterTest('ruby', 'packwerk')
+ call ale#test#SetFilename('../test-files/ruby/valid_rails_app/db/test.rb')
+
+ let g:ale_ruby_packwerk_executable = 'packwerk'
+ let g:ale_ruby_packwerk_options = ''
+
+ let b:sep = has('win32') ? '\' : '/'
+
+After:
+ unlet! b:sep
+
+ call ale#assert#TearDownLinterTest()
+
+Execute(Executable should default to packwerk):
+ AssertLinter 'packwerk', ale#Escape('packwerk')
+ \ . ' check '
+ \ . ale#Escape('db' . b:sep . 'test.rb')
+
+Execute(Should be able to set a custom executable):
+ let g:ale_ruby_packwerk_executable = 'bin/packwerk'
+
+ AssertLinter 'bin/packwerk', ale#Escape('bin/packwerk')
+ \ . ' check '
+ \ . ale#Escape('db' . b:sep . 'test.rb')
+
+Execute(Setting bundle appends 'exec packwerk'):
+ let g:ale_ruby_packwerk_executable = 'path to/bundle'
+
+ AssertLinter 'path to/bundle', ale#Escape('path to/bundle')
+ \ . ' exec packwerk'
+ \ . ' check '
+ \ . ale#Escape('db' . b:sep . 'test.rb')
+
+Execute(Command callback should be empty when not in a valid Rails app):
+ call ale#test#SetFilename('../test-files/ruby/not_a_rails_app/test.rb')
+
+ AssertLinter 'packwerk', ''