guard-slimlint 1.4.0 → 2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 14b281a7110ec5e1176f8e57dc91b18c1569b3eec1a14c19d17f7cf99ab814e2
4
- data.tar.gz: a2ec22b0621349fadca501979d70687eceb86b4232a25241d5e78ba9987d0611
3
+ metadata.gz: 47da81ec1db0fb4fa7809b0bd5403413f6572ab142508b9d8a979718f75aa873
4
+ data.tar.gz: a8309923aa75cd04905c61473d3e403c0ac9a0bc4e293a24aa1c8bc9ecd1d951
5
5
  SHA512:
6
- metadata.gz: 6096c002fa6518d50a22fcf29e9c98b9fc38f6b500f9cf043074feedd896c6e394bf96e3b949831044dd0517e6323de4a145ba90066c934443c3b68f67c01dde
7
- data.tar.gz: 2864c784c00d06c76fdae85d2c1ea2c4b9ea5a689853ad5d376cc391e74482fb75a1c2bbaef7155052b2b9f86e0cf974f9efac618a378ffaf7bc3ad9a7dfd4e1
6
+ metadata.gz: cd5617c218afc0f9bfcb3f9ef72b2b89fb3ca70d0ca108f7dce1d07ca8db921404aa8af7688cea10937747cdfcc8769087a74ed02a2d43bb84d6ae2cb4a2685f
7
+ data.tar.gz: 41a792aa16d6d22345126120c6df3bf5f0242271351d71f70b39f98ae1f77b1eecb664705e5468a016f8125b2176ecb2aa30ab77f75acfb61827403f355da26d
data/CHANGELOG.md CHANGED
@@ -6,6 +6,58 @@ follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [2.1.0] - 2026-09-14
10
+
11
+ ### Added
12
+
13
+ - `autocorrect:` option, default `false`, to automatically correct offences that
14
+ support it using `slim-lint -a`. Offences are corrected in-place on disk. If all
15
+ offences are corrected, the run succeeds cleanly.
16
+ - Non-boolean values passed to `autocorrect:`, `all_on_start:`, or `halt_on_fail:`
17
+ raise an `ArgumentError` on startup.
18
+
19
+ ## [2.0.0] - 2026-09-07
20
+
21
+ A behaviour release. The plugin still runs slim-lint as a subprocess, which
22
+ was a deliberate decision: Guard is a long-running process, and loading the
23
+ linter into it would freeze `.slim-lint.yml` at startup and let RuboCop's
24
+ caches grow all day. Four of the five comparable Guard plugins shell out for
25
+ the same reason.
26
+
27
+ ### Changed
28
+
29
+ - **`notify_on` now defaults to `:change`.** Notifications fire only when the
30
+ outcome flips, green to red or red to green. Saving a broken template twenty
31
+ times while fixing it produces one notification instead of twenty. Pass
32
+ `notify_on: :failure` for the old behaviour.
33
+ - **Ruby 3.3 or newer is required.** 3.2 reached end of life in March 2026.
34
+ - **`run_all` lints the directories Guard watches** rather than always
35
+ sweeping `.`. A `directories` line in your Guardfile now narrows it. Without
36
+ one, behaviour is unchanged.
37
+ - The Guardfile template watches `app/views` rather than every `.html.slim`
38
+ file anywhere in the project, and re-lints when `.slim-lint.yml` changes.
39
+ - Option readers are no longer writable. `notify_on` and `all_on_start` are
40
+ read once at construction, so the writers only ever looked useful.
41
+
42
+ ### Added
43
+
44
+ - `cli:` option, forwarding arbitrary arguments to slim-lint as a String or an
45
+ Array. This covers `-c`, linter selection and anything slim-lint adds later
46
+ without a new option here for each.
47
+ - `halt_on_fail:` option, default `true`, to opt out of failing Guard's task.
48
+ - An unrecognised `notify_on` now raises at startup instead of silently
49
+ disabling notifications for the whole session.
50
+ - Exit statuses that do not come from slim-lint itself now explain themselves.
51
+ Status 1 is Bundler failing to load the command, and 127 is a missing binary;
52
+ neither mentions Slim, so the bare number used to send people hunting for
53
+ lint errors in templates that were fine.
54
+
55
+ ### Removed
56
+
57
+ - The `colorize` runtime dependency. It monkeypatched `String` in every host
58
+ application to colour two log lines. Guard's own `Compat::UI.color` does the
59
+ same job, so the plugin now adds nothing to your object space.
60
+
9
61
  ## [1.4.0] - 2026-09-04
10
62
 
11
63
  The gem had not been touched since 2019 and could no longer be developed on any
@@ -75,7 +127,9 @@ without changing the plugin's interface.
75
127
  - `all_on_start` is honoured, so the plugin no longer lints everything on
76
128
  startup when the option is disabled.
77
129
 
78
- [Unreleased]: https://github.com/mike927/guard-slimlint/compare/v1.4.0...HEAD
130
+ [Unreleased]: https://github.com/mike927/guard-slimlint/compare/v2.1.0...HEAD
131
+ [2.1.0]: https://github.com/mike927/guard-slimlint/compare/v2.0.0...v2.1.0
132
+ [2.0.0]: https://github.com/mike927/guard-slimlint/compare/v1.4.0...v2.0.0
79
133
  [1.4.0]: https://github.com/mike927/guard-slimlint/compare/v1.3.2...v1.4.0
80
134
  [1.3.2]: https://github.com/mike927/guard-slimlint/compare/v1.3.1...v1.3.2
81
135
  [1.3.1]: https://github.com/mike927/guard-slimlint/compare/v1.3.0...v1.3.1
data/README.md CHANGED
@@ -6,7 +6,7 @@
6
6
  Guard::SlimLint runs [slim-lint](https://github.com/sds/slim-lint) automatically
7
7
  every time a Slim template is added or changed.
8
8
 
9
- Requires Ruby 3.1 or newer.
9
+ Requires Ruby 3.3 or newer.
10
10
 
11
11
  ## Installation
12
12
 
@@ -24,12 +24,6 @@ Then run:
24
24
  bundle install
25
25
  ```
26
26
 
27
- Or install it yourself:
28
-
29
- ```
30
- gem install guard-slimlint
31
- ```
32
-
33
27
  ## Usage
34
28
 
35
29
  Add the plugin to your Guardfile:
@@ -50,27 +44,48 @@ Linting itself is configured by slim-lint, not by this plugin. Put a
50
44
  ## Options
51
45
 
52
46
  ```ruby
53
- guard :slimlint, notify_on: :failure, all_on_start: true do
47
+ guard :slimlint, notify_on: :change, cli: '-c config/.slim-lint.yml' do
54
48
  watch(%r{^app/views/.+\.slim$})
49
+ watch(%r{(?:.+/)?\.slim-lint\.yml$}) { |m| File.dirname(m[0]) }
55
50
  end
56
51
  ```
57
52
 
58
53
  | Option | Default | Meaning |
59
54
  |--------|---------|---------|
60
- | `notify_on` | `:failure` | When to send a desktop notification. One of `:failure`, `:success`, `:both`, `:none`. |
55
+ | `notify_on` | `:change` | When to send a desktop notification. One of `:change`, `:failure`, `:success`, `:both`, `:none`. |
61
56
  | `all_on_start` | `true` | Lint everything once when Guard starts. |
57
+ | `halt_on_fail` | `true` | Tell Guard the task failed, so a group's `halt_on_fail` can stop the rest of it. |
58
+ | `autocorrect` | `false` | Automatically correct offences that support it using `slim-lint -a`. |
59
+ | `cli` | none | Extra arguments passed straight to slim-lint. String or Array. |
60
+
61
+ An unrecognised `notify_on` or non-boolean option raises at startup rather than silently going quiet.
62
+
63
+ ### Autocorrect
64
+
65
+ Setting `autocorrect: true` automatically runs `slim-lint -a`, fixing offences that support auto-correction directly on disk. If all offences in a file are corrected, slim-lint exits with status 0 and Guard reports success. If uncorrectable offences remain, slim-lint exits with status 65 and Guard reports the remaining offences.
66
+
67
+ Because auto-correction modifies files in-place, Guard's file listener detects the disk write and re-lints the now-clean template. The default `notify_on: :change` ensures this follow-up pass stays quiet and does not send duplicate desktop notifications.
68
+
69
+ ### Notifications
70
+
71
+ `:change` notifies only when the outcome flips, green to red or red to green.
72
+ Saving a broken file twenty times while you fix it produces one notification,
73
+ not twenty. `:failure` notifies on every failing run, which is the older
74
+ behaviour if you prefer it.
75
+
76
+ Notifications come from Guard itself. On macOS, add `terminal-notifier-guard`
77
+ to your Gemfile, or you will only see the terminal window title change.
62
78
 
63
- Desktop notifications come from Guard itself. On macOS, add
64
- `terminal-notifier-guard` to your Gemfile to get them.
79
+ ### Running everything
65
80
 
66
- Guard's `halt_on_fail` group option is respected. Put the plugin in a group with
67
- `halt_on_fail: true` to stop the rest of the group when linting fails.
81
+ `run_all` lints the directories Guard is watching, so a `directories` line in
82
+ your Guardfile narrows it. Without one it falls back to the whole project.
68
83
 
69
84
  ## Exit status handling
70
85
 
71
86
  slim-lint reports its outcome with sysexits codes. This plugin treats status 65
72
87
  as "offences were found" and every other non-zero status as slim-lint itself
73
- failing, which is reported through `Guard::UI.error` with the status. A
88
+ failing, which is reported through `Guard::Compat::UI.error` with the status. A
74
89
  misconfigured `.slim-lint.yml` or a crash is therefore not mistaken for a lint
75
90
  failure.
76
91
 
@@ -1,6 +1,14 @@
1
1
  # available options:
2
- # notify_on:
3
- # failure, success, both, none
4
- guard :slimlint, notify_on: :failure do
5
- watch(/^.+(\.html\.slim)$/)
2
+ # notify_on: change (default), failure, success, both, none
3
+ # all_on_start: true (default), false
4
+ # halt_on_fail: true (default), false
5
+ # autocorrect: false (default), true
6
+ # cli: extra arguments for slim-lint, e.g. '-c config/.slim-lint.yml'
7
+ guard :slimlint, notify_on: :change do
8
+ # Rails views. For a non-Rails project, watch wherever your templates live,
9
+ # for example watch(%r{^templates/.+\.slim$})
10
+ watch(%r{^app/views/.+\.slim$})
11
+
12
+ # Re-lint when the slim-lint configuration itself changes.
13
+ watch(%r{(?:.+/)?\.slim-lint\.yml$}) { |m| File.dirname(m[0]) }
6
14
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Guard
4
4
  class SlimLintVersion
5
- VERSION = '1.4.0'
5
+ VERSION = '2.1.0'
6
6
  end
7
7
  end
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'English'
4
+ require 'shellwords'
4
5
  require 'guard/compat/plugin'
5
- require 'colorize'
6
6
 
7
7
  module Guard
8
8
  class SlimLint < Plugin
@@ -12,11 +12,36 @@ module Guard
12
12
  EXIT_SUCCESS = 0
13
13
  EXIT_LINT_FAILURE = 65
14
14
 
15
- attr_accessor :notify_on, :all_on_start
15
+ # Recognised :notify_on values. :change notifies only when the outcome
16
+ # flips, which is what stops a long editing session from producing one
17
+ # notification per save.
18
+ NOTIFY_MODES = %i[change failure success both none].freeze
19
+
20
+ # Statuses that are not slim-lint's own sysexits codes, paired with what a
21
+ # user is most likely looking at. Bundler exits 1 when it cannot load the
22
+ # command, and a shell exits 127 when the binary is missing; neither says
23
+ # anything about Slim, so the bare number sends people hunting for lint
24
+ # errors that do not exist.
25
+ STATUS_HINTS = {
26
+ 1 => 'the bundle is probably out of sync, try bundle install',
27
+ 127 => 'slim-lint is not on PATH, check that the gem is installed'
28
+ }.freeze
29
+
30
+ AUTOCORRECT_FLAGS = %w[-a --autocorrect --auto-correct -A --autocorrect-all].freeze
31
+
32
+ attr_reader :notify_on, :all_on_start, :halt_on_fail, :cli, :autocorrect
16
33
 
17
34
  def initialize(options = {})
18
- @notify_on = options.fetch(:notify_on, :failure)
35
+ @notify_on = options.fetch(:notify_on, :change)
19
36
  @all_on_start = options.fetch(:all_on_start, true)
37
+ @halt_on_fail = options.fetch(:halt_on_fail, true)
38
+ @cli = options[:cli]
39
+ @autocorrect = options.fetch(:autocorrect, false)
40
+ # Assume the project starts green, so a clean first run stays quiet
41
+ # under :change instead of announcing that nothing is wrong.
42
+ @last_success = true
43
+
44
+ validate_options!
20
45
  super
21
46
  end
22
47
 
@@ -25,7 +50,7 @@ module Guard
25
50
  end
26
51
 
27
52
  def run_all
28
- run
53
+ run(lint_targets)
29
54
  end
30
55
 
31
56
  def run_on_modifications(paths)
@@ -38,7 +63,41 @@ module Guard
38
63
 
39
64
  private
40
65
 
41
- def run(paths = ['.'])
66
+ def validate_options!
67
+ validate_notify_on!
68
+ validate_boolean!(:all_on_start, all_on_start)
69
+ validate_boolean!(:halt_on_fail, halt_on_fail)
70
+ validate_boolean!(:autocorrect, autocorrect)
71
+ end
72
+
73
+ def validate_notify_on!
74
+ return if NOTIFY_MODES.include?(notify_on)
75
+
76
+ raise ArgumentError,
77
+ "unknown :notify_on #{notify_on.inspect}, expected one of " \
78
+ "#{NOTIFY_MODES.map(&:inspect).join(', ')}"
79
+ end
80
+
81
+ def validate_boolean!(name, value)
82
+ return if [true, false].include?(value)
83
+
84
+ raise ArgumentError, ":#{name} must be true or false, got #{value.inspect}"
85
+ end
86
+
87
+ # The directories Guard was told to watch, so run_all honours a
88
+ # `directories` line in the Guardfile instead of always sweeping the
89
+ # whole project.
90
+ def lint_targets
91
+ dirs = Compat.watched_directories.map(&:to_s)
92
+ dirs.empty? ? ['.'] : dirs
93
+ rescue NotImplementedError
94
+ # guard-compat raises this when Guard's CLI is not loaded, which
95
+ # happens when the plugin is driven programmatically rather than by
96
+ # `guard`. Linting the whole project is what run_all did before 2.0.0.
97
+ ['.']
98
+ end
99
+
100
+ def run(paths)
42
101
  status = lint(paths)
43
102
 
44
103
  case status
@@ -54,57 +113,65 @@ module Guard
54
113
  # @param paths [Array<String>]
55
114
  # @return [Integer, nil] the exit status, or nil if slim-lint never ran
56
115
  def lint(paths)
57
- system('slim-lint', *paths)
116
+ system('slim-lint', *cli_args, *paths)
58
117
  $CHILD_STATUS&.exitstatus
59
118
  end
60
119
 
120
+ def cli_args
121
+ args = case cli
122
+ when Array then cli.map(&:to_s)
123
+ when String then cli.shellsplit
124
+ else []
125
+ end
126
+
127
+ args.unshift('-a') if autocorrect && !args.intersect?(AUTOCORRECT_FLAGS)
128
+ args
129
+ end
130
+
61
131
  def report_success
62
- UI.info 'No Slim offences detected'.green
63
- check_and_notify(true)
132
+ Compat::UI.info(Compat::UI.color('No Slim offences detected', :green))
133
+ finish(true, 'No slim offences')
64
134
  end
65
135
 
66
136
  def report_offences
67
- UI.info 'Slim offences have been detected'.red
68
- check_and_notify(false)
69
- throw :task_has_failed
137
+ Compat::UI.info(Compat::UI.color('Slim offences have been detected', :red))
138
+ finish(false, 'Slim offences detected')
70
139
  end
71
140
 
72
141
  def report_error(status)
73
142
  message = error_message(status)
74
- UI.error message
75
- check_and_notify(false, message)
76
- throw :task_has_failed
143
+ Compat::UI.error(message)
144
+ finish(false, message)
77
145
  end
78
146
 
79
147
  def error_message(status)
80
148
  return 'slim-lint could not be run' if status.nil?
81
149
 
82
- "slim-lint exited with status #{status}"
150
+ message = "slim-lint exited with status #{status}"
151
+ hint = STATUS_HINTS[status]
152
+ hint ? "#{message}: #{hint}" : message
153
+ end
154
+
155
+ # Notifies if the configured mode calls for it, records the outcome so
156
+ # :change can compare against it next time, then hands back to Guard.
157
+ def finish(success, message)
158
+ notify(success, message) if notification_allowed?(success)
159
+ @last_success = success
160
+ throw :task_has_failed if halt_on_fail && !success
83
161
  end
84
162
 
85
- def notification_allowed?(result)
163
+ def notification_allowed?(success)
86
164
  case notify_on
87
- when :failure then !result
88
- when :success then result
165
+ when :change then success != @last_success
166
+ when :failure then !success
167
+ when :success then success
89
168
  when :both then true
90
169
  else false
91
170
  end
92
171
  end
93
172
 
94
- def check_and_notify(result, message = default_message(result))
95
- notify(result, message) if notification_allowed?(result)
96
- end
97
-
98
- def image(result)
99
- result ? :success : :failed
100
- end
101
-
102
- def default_message(result)
103
- result ? 'No slim offences' : 'Slim offences detected'
104
- end
105
-
106
- def notify(result, message = default_message(result))
107
- Notifier.notify(message, title: 'Slim-lint results', image: image(result))
173
+ def notify(success, message)
174
+ Compat::UI.notify(message, title: 'Slim-lint results', image: success ? :success : :failed)
108
175
  end
109
176
  end
110
177
  end
metadata CHANGED
@@ -1,35 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: guard-slimlint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 2.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michal Gajowiak
8
- autorequire:
9
8
  bindir: bin
10
9
  cert_chain: []
11
- date: 2026-09-04 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  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.8'
20
- - - "<"
21
- - !ruby/object:Gem::Version
22
- version: '2.0'
23
- type: :runtime
24
- prerelease: false
25
- version_requirements: !ruby/object:Gem::Requirement
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- version: '0.8'
30
- - - "<"
31
- - !ruby/object:Gem::Version
32
- version: '2.0'
33
12
  - !ruby/object:Gem::Dependency
34
13
  name: guard
35
14
  requirement: !ruby/object:Gem::Requirement
@@ -171,7 +150,6 @@ metadata:
171
150
  changelog_uri: https://github.com/mike927/guard-slimlint/blob/master/CHANGELOG.md
172
151
  bug_tracker_uri: https://github.com/mike927/guard-slimlint/issues
173
152
  rubygems_mfa_required: 'true'
174
- post_install_message:
175
153
  rdoc_options: []
176
154
  require_paths:
177
155
  - lib
@@ -179,15 +157,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
179
157
  requirements:
180
158
  - - ">="
181
159
  - !ruby/object:Gem::Version
182
- version: '3.1'
160
+ version: '3.3'
183
161
  required_rubygems_version: !ruby/object:Gem::Requirement
184
162
  requirements:
185
163
  - - ">="
186
164
  - !ruby/object:Gem::Version
187
165
  version: '0'
188
166
  requirements: []
189
- rubygems_version: 3.4.10
190
- signing_key:
167
+ rubygems_version: 3.6.9
191
168
  specification_version: 4
192
169
  summary: Guard plugin that runs slim-lint automatically
193
170
  test_files: []