rubocop-exception_messages 0.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: fc8af98d6bdcef4ebe89488a40c7bbb813ca3a9cbc6c6c87e7ad9fa5a09a1127
4
+ data.tar.gz: e6f89e17ee748d3f978f4bf332a1ec7d3d9d6f1731ce6d5dbdf2fdb6bb39c852
5
+ SHA512:
6
+ metadata.gz: debf11ec95b5b9533c58d471b44d142747437fe951c797fa5734723d6eab32ae945e511b24a179590d822155e859f85d983a85365af8a2b6cb3b858353df4b7b
7
+ data.tar.gz: f8cf2d88a4542efc375d5e9074f0efcee550266a2f69d18ada7dfccadddeb66850b6e7be8df8056d166f0a25c6f49f89bb539e13bb4b9c5984266b2124935ba0
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ ### 0.1.0 (2026/09/05)
2
+
3
+ #### Features
4
+
5
+ * Initial release with `ExceptionMessages/Casing` and `ExceptionMessages/Punctuation` cops - [@dblock](https://github.com/dblock).
6
+ * Add Danger CI check that enforces a CHANGELOG entry on every pull request - [@dblock](https://github.com/dblock).
7
+ * Add RELEASING.md - [@dblock](https://github.com/dblock).
8
+ * [#2](https://github.com/dblock/rubocop-exception_messages/pull/2): Add test coverage reporting with SimpleCov and Coveralls - [@dblock](https://github.com/dblock).
9
+ * [#3](https://github.com/dblock/rubocop-exception_messages/pull/3): Split RuboCop linting into its own `Lint` CI workflow, separate from `Test` - [@dblock](https://github.com/dblock).
10
+ * [#4](https://github.com/dblock/rubocop-exception_messages/pull/4): Test against Ruby 3.2, 3.3, 3.4 and 4.0 in CI - [@dblock](https://github.com/dblock).
11
+
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,76 @@
1
+ Contributing to rubocop-exception_messages
2
+ ===========================================
3
+
4
+ This gem follows the same contribution flow as most Ruby open-source projects.
5
+
6
+ #### Fork the Project
7
+
8
+ Fork the [project on Github](https://github.com/dblock/rubocop-exception_messages) and check out your copy.
9
+
10
+ ```
11
+ git clone https://github.com/contributor/rubocop-exception_messages.git
12
+ cd rubocop-exception_messages
13
+ git remote add upstream https://github.com/dblock/rubocop-exception_messages.git
14
+ ```
15
+
16
+ #### Create a Topic Branch
17
+
18
+ Make sure your fork is up-to-date and create a topic branch for your feature or bug fix.
19
+
20
+ ```
21
+ git checkout main
22
+ git pull upstream main
23
+ git checkout -b my-feature-branch
24
+ ```
25
+
26
+ #### Bundle Install and Test
27
+
28
+ Ensure that you can build the project and run tests.
29
+
30
+ ```
31
+ bundle install
32
+ bundle exec rake
33
+ ```
34
+
35
+ #### Write Tests
36
+
37
+ Add specs for new or changed cop behavior under [spec/rubocop/cop/exception_messages](spec/rubocop/cop/exception_messages), using RuboCop's `expect_offense`/`expect_correction` helpers.
38
+
39
+ #### Write Code
40
+
41
+ Implement your feature or bug fix. Ruby style is enforced with RuboCop itself: run `bundle exec rubocop` and fix any style issues highlighted.
42
+
43
+ Make sure that `bundle exec rake` completes without errors.
44
+
45
+ #### Update Changelog
46
+
47
+ Add a line to [CHANGELOG.md](CHANGELOG.md) under *Next Release*, including your name and a link to your Github account.
48
+
49
+ #### Commit Changes
50
+
51
+ Make sure git knows your name and email address:
52
+
53
+ ```
54
+ git config --global user.name "Your Name"
55
+ git config --global user.email "contributor@example.com"
56
+ ```
57
+
58
+ Writing good commit messages is important. A commit message should describe what changed, why, and reference issues fixed (if any).
59
+
60
+ #### Push
61
+
62
+ ```
63
+ git push origin my-feature-branch
64
+ ```
65
+
66
+ #### Make a Pull Request
67
+
68
+ Go to https://github.com/contributor/rubocop-exception_messages and select your feature branch, then click the pull request button and fill out the form.
69
+
70
+ #### Be Patient
71
+
72
+ It may take time for a maintainer to respond. Please be patient and allow for time to review contributions.
73
+
74
+ #### Thank You
75
+
76
+ Please do know that we really appreciate and value your time and work. We love you, really.
data/Dangerfile ADDED
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ changelog.check!
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Daniel (dB.) Doubrovkine
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # rubocop-exception_messages
2
+
3
+ [![Ruby](https://github.com/dblock/rubocop-exception_messages/actions/workflows/test.yml/badge.svg)](https://github.com/dblock/rubocop-exception_messages/actions/workflows/test.yml)
4
+ [![Coverage Status](https://coveralls.io/repos/github/dblock/rubocop-exception_messages/badge.svg?branch=master)](https://coveralls.io/github/dblock/rubocop-exception_messages?branch=master)
5
+
6
+ RuboCop cops that standardize the style of raised exception messages, consistent with Ruby's own core and standard library exceptions (e.g. `TypeError: no implicit conversion from nil to integer`, `ArgumentError: wrong number of arguments`).
7
+
8
+ ## Rationale
9
+
10
+ Ruby's built-in exceptions never capitalize or punctuate their messages. This reads naturally when Ruby prints the exception class name, a colon, and the message together in a backtrace (`ArgumentError: block is required`, not `ArgumentError: Block is required.`). These cops help keep custom `raise` messages consistent with that convention.
11
+
12
+ ## Installation
13
+
14
+ Add to your `Gemfile`:
15
+
16
+ ```ruby
17
+ group :development do
18
+ gem "rubocop-exception_messages", require: false
19
+ end
20
+ ```
21
+
22
+ Then require it in your `.rubocop.yml`:
23
+
24
+ ```yaml
25
+ plugins:
26
+ - rubocop-exception_messages
27
+ ```
28
+
29
+ ## Cops
30
+
31
+ ### ExceptionMessages/Casing
32
+
33
+ Checks that raised exception messages start with a lowercase letter.
34
+
35
+ ```ruby
36
+ # bad
37
+ raise ArgumentError, "Block is required"
38
+ raise ArgumentError.new("Block is required")
39
+
40
+ # good
41
+ raise ArgumentError, "block is required"
42
+ raise ArgumentError.new("block is required")
43
+ ```
44
+
45
+ ### ExceptionMessages/Punctuation
46
+
47
+ Checks that raised exception messages do not end with a period.
48
+
49
+ ```ruby
50
+ # bad
51
+ raise ArgumentError, "block is required."
52
+ raise ArgumentError.new("block is required.")
53
+
54
+ # good
55
+ raise ArgumentError, "block is required"
56
+ raise ArgumentError.new("block is required")
57
+ ```
58
+
59
+ Both cops support autocorrection (`rubocop -A`).
60
+
61
+ ## Contributing
62
+
63
+ See [CONTRIBUTING.md](CONTRIBUTING.md).
64
+
65
+ ## Copyright and License
66
+
67
+ MIT License, see [LICENSE](LICENSE.txt) for details.
data/RELEASING.md ADDED
@@ -0,0 +1,86 @@
1
+ Releasing rubocop-exception_messages
2
+ =====================================
3
+
4
+ There're no particular rules about when to release rubocop-exception_messages. Release bug fixes frequently, features not so frequently and breaking API changes rarely.
5
+
6
+ ### Release
7
+
8
+ Run tests, check that all tests succeed locally.
9
+
10
+ ```
11
+ bundle install
12
+ bundle exec rake
13
+ ```
14
+
15
+ Double-check that the [last build succeeded](https://github.com/dblock/rubocop-exception_messages/actions) in GitHub Actions.
16
+
17
+ Change "Next" in [CHANGELOG.md](CHANGELOG.md) to the new version.
18
+
19
+ ```
20
+ ### 0.1.0 (2026/01/01)
21
+ ```
22
+
23
+ Remove the line with "* Your contribution here.", since there will be no more contributions to this release.
24
+
25
+ Bump the version in [lib/rubocop/exception_messages/version.rb](lib/rubocop/exception_messages/version.rb).
26
+
27
+ ```ruby
28
+ module RuboCop
29
+ module ExceptionMessages
30
+ VERSION = '0.1.0'
31
+ end
32
+ end
33
+ ```
34
+
35
+ Commit your changes.
36
+
37
+ ```
38
+ git add CHANGELOG.md lib/rubocop/exception_messages/version.rb
39
+ git commit -m "Preparing for release, 0.1.0."
40
+ git push origin master
41
+ ```
42
+
43
+ Release.
44
+
45
+ ```
46
+ $ bundle exec rake release
47
+
48
+ rubocop-exception_messages 0.1.0 built to pkg/rubocop-exception_messages-0.1.0.gem.
49
+ Tagged v0.1.0.
50
+ Pushed git commits and tags.
51
+ Pushed rubocop-exception_messages 0.1.0 to rubygems.org.
52
+ ```
53
+
54
+ ### Prepare for the Next Version
55
+
56
+ Add the next release to [CHANGELOG.md](CHANGELOG.md).
57
+
58
+ ```
59
+ ### 0.1.1 (Next)
60
+
61
+ #### Features
62
+
63
+ * Your contribution here.
64
+
65
+ #### Fixes
66
+
67
+ * Your contribution here.
68
+ ```
69
+
70
+ Bump the version in [lib/rubocop/exception_messages/version.rb](lib/rubocop/exception_messages/version.rb).
71
+
72
+ ```ruby
73
+ module RuboCop
74
+ module ExceptionMessages
75
+ VERSION = '0.1.1'
76
+ end
77
+ end
78
+ ```
79
+
80
+ Commit your changes.
81
+
82
+ ```
83
+ git add CHANGELOG.md lib/rubocop/exception_messages/version.rb
84
+ git commit -m "Preparing for next development iteration, 0.1.1."
85
+ git push origin master
86
+ ```
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,12 @@
1
+ ExceptionMessages:
2
+ Enabled: true
3
+
4
+ ExceptionMessages/Casing:
5
+ Description: 'Checks that raised exception messages start with a lowercase letter.'
6
+ Enabled: true
7
+ VersionAdded: '0.1'
8
+
9
+ ExceptionMessages/Punctuation:
10
+ Description: 'Checks that raised exception messages do not end with a period.'
11
+ Enabled: true
12
+ VersionAdded: '0.1'
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module ExceptionMessages
6
+ # Checks that raised exception messages start with a lowercase letter,
7
+ # consistent with Ruby's own core and standard library exceptions
8
+ # (e.g. `TypeError: no implicit conversion from nil to integer`).
9
+ #
10
+ # @example
11
+ # # bad
12
+ # raise ArgumentError, "Block is required"
13
+ # raise ArgumentError.new("Block is required")
14
+ #
15
+ # # good
16
+ # raise ArgumentError, "block is required"
17
+ # raise ArgumentError.new("block is required")
18
+ class Casing < Base
19
+ include MessageNode
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Exception messages should start with a lowercase letter.'
23
+
24
+ def on_send(node)
25
+ message_node = raise_message_node(node)
26
+ return unless message_node
27
+
28
+ first_segment = first_string_segment(message_node)
29
+ return unless first_segment
30
+
31
+ content = first_segment.value
32
+ return if content.empty? || content[0] !~ /[A-Z]/
33
+
34
+ add_offense(message_node) do |corrector|
35
+ corrected = content.sub(/\A./, &:downcase)
36
+ corrector.replace(first_segment.loc.expression, first_segment.source.sub(content, corrected))
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def first_string_segment(message_node)
43
+ segment = message_node.str_type? ? message_node : message_node.child_nodes.first
44
+ segment if segment&.str_type?
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module ExceptionMessages
6
+ # Shared helpers for locating the string literal passed to `raise`.
7
+ module MessageNode
8
+ private
9
+
10
+ def raise_message_node(node)
11
+ return unless node.method?(:raise)
12
+
13
+ message_arg = extract_message_arg(node.arguments)
14
+ return unless message_arg
15
+ return unless message_arg.str_type? || message_arg.dstr_type?
16
+
17
+ message_arg
18
+ end
19
+
20
+ def extract_message_arg(args)
21
+ return if args.empty?
22
+
23
+ first_arg = args.first
24
+ return first_arg.arguments.first if first_arg.send_type? && first_arg.method?(:new)
25
+
26
+ args[1]
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module ExceptionMessages
6
+ # Checks that raised exception messages do not end with a period,
7
+ # consistent with Ruby's own core and standard library exceptions
8
+ # (e.g. `ArgumentError: wrong number of arguments`).
9
+ #
10
+ # @example
11
+ # # bad
12
+ # raise ArgumentError, "block is required."
13
+ # raise ArgumentError.new("block is required.")
14
+ #
15
+ # # good
16
+ # raise ArgumentError, "block is required"
17
+ # raise ArgumentError.new("block is required")
18
+ class Punctuation < Base
19
+ include MessageNode
20
+ extend AutoCorrector
21
+
22
+ MSG = 'Exception messages should not end with a period.'
23
+
24
+ def on_send(node)
25
+ message_node = raise_message_node(node)
26
+ return unless message_node
27
+
28
+ last_segment = last_string_segment(message_node)
29
+ return unless last_segment
30
+
31
+ check_segment(message_node, last_segment)
32
+ end
33
+
34
+ private
35
+
36
+ def check_segment(message_node, segment)
37
+ content = segment.value
38
+ return unless content.end_with?('.')
39
+ return if content.end_with?('..') # ellipsis-style, not a sentence-ending period
40
+
41
+ add_offense(message_node) do |corrector|
42
+ corrected = content.sub(/\.\z/, '')
43
+ corrector.replace(segment.loc.expression, segment.source.sub(content, corrected))
44
+ end
45
+ end
46
+
47
+ def last_string_segment(message_node)
48
+ segment = message_node.str_type? ? message_node : message_node.child_nodes.last
49
+ segment if segment&.str_type?
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'exception_messages/message_node'
4
+ require_relative 'exception_messages/casing'
5
+ require_relative 'exception_messages/punctuation'
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lint_roller'
4
+
5
+ module RuboCop
6
+ module ExceptionMessages
7
+ # A plugin that integrates RuboCop ExceptionMessages with RuboCop's plugin system.
8
+ class Plugin < LintRoller::Plugin
9
+ def about
10
+ LintRoller::About.new(
11
+ name: 'rubocop-exception_messages',
12
+ version: VERSION,
13
+ homepage: 'https://github.com/dblock/rubocop-exception_messages',
14
+ description: 'RuboCop cops that standardize the style of raised exception messages.'
15
+ )
16
+ end
17
+
18
+ def supported?(context)
19
+ context.engine == :rubocop
20
+ end
21
+
22
+ def rules(_context)
23
+ project_root = Pathname.new(__dir__).join('../../..')
24
+
25
+ LintRoller::Rules.new(type: :path, config_format: :rubocop, value: project_root.join('config', 'default.yml'))
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module ExceptionMessages
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ # RuboCop cops for standardizing the style of raised exception messages.
5
+ module ExceptionMessages
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rubocop'
4
+
5
+ require_relative 'rubocop/exception_messages/version'
6
+ require_relative 'rubocop/cop/exception_messages_cops'
7
+ require_relative 'rubocop/exception_messages'
8
+ require_relative 'rubocop/exception_messages/plugin'
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rubocop-exception_messages
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Daniel (dB.) Doubrovkine
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2026-09-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lint_roller
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rubocop
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.72'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.72'
41
+ description: RuboCop cops that check raised exception messages start with a lowercase
42
+ letter and do not end with a period, consistent with Ruby's own core and standard
43
+ library exceptions.
44
+ email:
45
+ - dblock@dblock.org
46
+ executables: []
47
+ extensions: []
48
+ extra_rdoc_files: []
49
+ files:
50
+ - CHANGELOG.md
51
+ - CONTRIBUTING.md
52
+ - Dangerfile
53
+ - LICENSE.txt
54
+ - README.md
55
+ - RELEASING.md
56
+ - Rakefile
57
+ - config/default.yml
58
+ - lib/rubocop-exception_messages.rb
59
+ - lib/rubocop/cop/exception_messages/casing.rb
60
+ - lib/rubocop/cop/exception_messages/message_node.rb
61
+ - lib/rubocop/cop/exception_messages/punctuation.rb
62
+ - lib/rubocop/cop/exception_messages_cops.rb
63
+ - lib/rubocop/exception_messages.rb
64
+ - lib/rubocop/exception_messages/plugin.rb
65
+ - lib/rubocop/exception_messages/version.rb
66
+ homepage: https://github.com/dblock/rubocop-exception_messages
67
+ licenses:
68
+ - MIT
69
+ metadata:
70
+ allowed_push_host: https://rubygems.org
71
+ homepage_uri: https://github.com/dblock/rubocop-exception_messages
72
+ source_code_uri: https://github.com/dblock/rubocop-exception_messages
73
+ changelog_uri: https://github.com/dblock/rubocop-exception_messages/blob/main/CHANGELOG.md
74
+ rubygems_mfa_required: 'true'
75
+ default_lint_roller_plugin: RuboCop::ExceptionMessages::Plugin
76
+ post_install_message:
77
+ rdoc_options: []
78
+ require_paths:
79
+ - lib
80
+ required_ruby_version: !ruby/object:Gem::Requirement
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ version: 3.2.0
85
+ required_rubygems_version: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ requirements: []
91
+ rubygems_version: 3.5.16
92
+ signing_key:
93
+ specification_version: 4
94
+ summary: RuboCop cops for standardizing the style of raised exception messages.
95
+ test_files: []