crap4ruby 0.1.0 → 0.2.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: 55e5f45f41b35122c6ad4b4894e3d5e496af3842c4a4f28e8172806c45d8d620
4
- data.tar.gz: '0279b34834aeba13649a0f124b6064b44a92fdffa868ad5f1a893e56a9067473'
3
+ metadata.gz: e5227286a59e47edaf5fe8e20c8ce9c1abe6391b4f95304f8352c6b98fdf53dd
4
+ data.tar.gz: 2a569cea7aa8f9172d2ce2c854dec0d62c76ae3e73f1f3618f911b3d55c6deff
5
5
  SHA512:
6
- metadata.gz: 555c402719177c91525c68611ee3e5e88ae0c674bf74a1f2b477857d4e97e017cfe50a2674d829e46a9fd7c16a252d69aaf97953ebb811d6a98a733596898157
7
- data.tar.gz: 0e9ea5f5d64473c6547faaad0cc4126a0d7a1c0236c37c0a457cdb1e69ae5d828160619ddce843033df76c723c45440f64049b071a5bf74846ad11447700a1d4
6
+ metadata.gz: 25f98c5d9ca08faa51388483dc63735238f4dc0a20daf13a80ad104086e48c36d91fb2204a2d4e25a903ce17ccdd510cb8059be09b47ea4a582921fee1a5b649
7
+ data.tar.gz: b819b05ff07ec266c2316f77b317b2f45975e4ebd7ed2e4f325aea8553a2fa767033d4351086ab67979f9230523497f1d91aab3f49fad540ae911c529fd4dbce
data/CHANGELOG.md CHANGED
@@ -1,7 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ All notable changes are recorded here using [Keep a Changelog 2.0.0](https://keepachangelog.com/en/2.0.0/).
4
+ Version numbers follow [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) as defined in the [versioning policy](VERSIONING.md).
5
+
3
6
  ## [Unreleased]
4
7
 
8
+ ## [0.2.0] - 2026-10-28
9
+
10
+ ### Changed
11
+
12
+ - Use RuboCop 1.92.1 complexity rules, including iteration blocks, safe-navigation discounts, rescue groups, and nested bodies, while excluding a method's own parameter defaults.
13
+ - Discover bare `define_method` calls with literal symbol or string names and blocks.
14
+ - Reject literal `define_method` names that cannot be represented in UTF-8, preserving the previous snapshot.
15
+ - Report N/A for coverage and CRAP when a body shares its declaration line. Unknown scores pass a requested threshold only when their maximum possible score fits it; otherwise the check returns exit 1. These scoring and exit-code changes require a minor release. Regenerate coverage and snapshots and review thresholds when upgrading.
16
+
17
+ ### Fixed
18
+
19
+ - Include executable interpolation lines when a method ends with a heredoc.
20
+ - Discover explicit method definitions inside parameter defaults and superclass expressions.
21
+ - Compare exact CRAP scores with decimal thresholds without floating-point rounding errors.
22
+ - Avoid attributing trailing code on a method's closing line to the method, and escape control characters in displayed method names.
23
+
24
+ ## [0.1.1] - 2026-10-28
25
+
26
+ ### Changed
27
+
28
+ - Document single-file analysis, contribution and private reporting policies, and the public compatibility policy.
29
+ - Add a YARD maintainer reference and link to gem documentation from RubyGems metadata.
30
+
5
31
  ## [0.1.0] - 2026-10-28
6
32
 
7
33
  ### Added
@@ -16,5 +42,7 @@
16
42
 
17
43
  - Split CLI execution, AST discovery, and coverage loading into smaller methods without changing their behavior or scoring rules.
18
44
 
19
- [Unreleased]: https://github.com/svyatov/crap4ruby/compare/v0.1.0...HEAD
45
+ [Unreleased]: https://github.com/svyatov/crap4ruby/compare/v0.2.0...HEAD
46
+ [0.2.0]: https://github.com/svyatov/crap4ruby/compare/v0.1.1...v0.2.0
47
+ [0.1.1]: https://github.com/svyatov/crap4ruby/compare/v0.1.0...v0.1.1
20
48
  [0.1.0]: https://github.com/svyatov/crap4ruby/releases/tag/v0.1.0
@@ -0,0 +1,90 @@
1
+ # Contributor Covenant 3.0 Code of Conduct
2
+
3
+ ## Our Pledge
4
+
5
+ We pledge to make our community welcoming, safe, and equitable for all.
6
+
7
+ We are committed to fostering an environment that respects and promotes the dignity, rights, and contributions of all individuals, regardless of characteristics including race, ethnicity, caste, color, age, physical characteristics, neurodiversity, disability, sex or gender, gender identity or expression, sexual orientation, language, philosophy or religion, national or social origin, socio-economic position, level of education, or other status. The same privileges of participation are extended to everyone who participates in good faith and in accordance with this Covenant.
8
+
9
+
10
+ ## Encouraged Behaviors
11
+
12
+ While acknowledging differences in social norms, we all strive to meet our community's expectations for positive behavior. We also understand that our words and actions may be interpreted differently than we intend based on culture, background, or native language.
13
+
14
+ With these considerations in mind, we agree to behave mindfully toward each other and act in ways that center our shared values, including:
15
+
16
+ 1. Respecting the **purpose of our community**, our activities, and our ways of gathering.
17
+ 2. Engaging **kindly and honestly** with others.
18
+ 3. Respecting **different viewpoints** and experiences.
19
+ 4. **Taking responsibility** for our actions and contributions.
20
+ 5. Gracefully giving and accepting **constructive feedback**.
21
+ 6. Committing to **repairing harm** when it occurs.
22
+ 7. Behaving in other ways that promote and sustain the **well-being of our community**.
23
+
24
+
25
+ ## Restricted Behaviors
26
+
27
+ We agree to restrict the following behaviors in our community. Instances, threats, and promotion of these behaviors are violations of this Code of Conduct.
28
+
29
+ 1. **Harassment.** Violating explicitly expressed boundaries or engaging in unnecessary personal attention after any clear request to stop.
30
+ 2. **Character attacks.** Making insulting, demeaning, or pejorative comments directed at a community member or group of people.
31
+ 3. **Stereotyping or discrimination.** Characterizing anyone’s personality or behavior on the basis of immutable identities or traits.
32
+ 4. **Sexualization.** Behaving in a way that would generally be considered inappropriately intimate in the context or purpose of the community.
33
+ 5. **Violating confidentiality**. Sharing or acting on someone's personal or private information without their permission.
34
+ 6. **Endangerment.** Causing, encouraging, or threatening violence or other harm toward any person or group.
35
+ 7. Behaving in other ways that **threaten the well-being** of our community.
36
+
37
+ ### Other Restrictions
38
+
39
+ 1. **Misleading identity.** Impersonating someone else for any reason, or pretending to be someone else to evade enforcement actions.
40
+ 2. **Failing to credit sources.** Not properly crediting the sources of content you contribute.
41
+ 3. **Promotional materials**. Sharing marketing or other commercial content in a way that is outside the norms of the community.
42
+ 4. **Irresponsible communication.** Failing to responsibly present content which includes, links or describes any other restricted behaviors.
43
+
44
+
45
+ ## Reporting an Issue
46
+
47
+ Tensions can occur between community members even when they are trying their best to collaborate. Not every conflict represents a code of conduct violation, and this Code of Conduct reinforces encouraged behaviors and norms that can help avoid conflicts and minimize harm.
48
+
49
+ When an incident does occur, it is important to report it promptly. To report a possible violation, email [leonid@svyatov.com](mailto:leonid@svyatov.com). Leonid Svyatov monitors this address and will respond within 14 days.
50
+
51
+ Community Moderators take reports of violations seriously and will make every effort to respond in a timely manner. They will investigate all reports of code of conduct violations, reviewing messages, logs, and recordings, or interviewing witnesses and other participants. Community Moderators will keep investigation and enforcement actions as transparent as possible while prioritizing safety and confidentiality. In order to honor these values, enforcement actions are carried out in private with the involved parties, but communicating to the whole community may be part of a mutually agreed upon resolution.
52
+
53
+
54
+ ## Addressing and Repairing Harm
55
+
56
+ If an investigation by the Community Moderators finds that this Code of Conduct has been violated, the following enforcement ladder may be used to determine how best to repair harm, based on the incident's impact on the individuals involved and the community as a whole. Depending on the severity of a violation, lower rungs on the ladder may be skipped.
57
+
58
+ 1) Warning
59
+ 1) Event: A violation involving a single incident or series of incidents.
60
+ 2) Consequence: A private, written warning from the Community Moderators.
61
+ 3) Repair: Examples of repair include a private written apology, acknowledgement of responsibility, and seeking clarification on expectations.
62
+ 2) Temporarily Limited Activities
63
+ 1) Event: A repeated incidence of a violation that previously resulted in a warning, or the first incidence of a more serious violation.
64
+ 2) Consequence: A private, written warning with a time-limited cooldown period designed to underscore the seriousness of the situation and give the community members involved time to process the incident. The cooldown period may be limited to particular communication channels or interactions with particular community members.
65
+ 3) Repair: Examples of repair may include making an apology, using the cooldown period to reflect on actions and impact, and being thoughtful about re-entering community spaces after the period is over.
66
+ 3) Temporary Suspension
67
+ 1) Event: A pattern of repeated violation which the Community Moderators have tried to address with warnings, or a single serious violation.
68
+ 2) Consequence: A private written warning with conditions for return from suspension. In general, temporary suspensions give the person being suspended time to reflect upon their behavior and possible corrective actions.
69
+ 3) Repair: Examples of repair include respecting the spirit of the suspension, meeting the specified conditions for return, and being thoughtful about how to reintegrate with the community when the suspension is lifted.
70
+ 4) Permanent Ban
71
+ 1) Event: A pattern of repeated code of conduct violations that other steps on the ladder have failed to resolve, or a violation so serious that the Community Moderators determine there is no way to keep the community safe with this person as a member.
72
+ 2) Consequence: Access to all community spaces, tools, and communication channels is removed. In general, permanent bans should be rarely used, should have strong reasoning behind them, and should only be resorted to if working through other remedies has failed to change the behavior.
73
+ 3) Repair: There is no possible repair in cases of this severity.
74
+
75
+ This enforcement ladder is intended as a guideline. It does not limit the ability of Community Managers to use their discretion and judgment, in keeping with the best interests of our community.
76
+
77
+
78
+ ## Scope
79
+
80
+ This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public or other spaces. Examples of representing our community include using an official email address, posting via an official social media account, or acting as an appointed representative at an online or offline event.
81
+
82
+
83
+ ## Attribution
84
+
85
+ This Code of Conduct is adapted from the Contributor Covenant, version 3.0, permanently available at [https://www.contributor-covenant.org/version/3/0/](https://www.contributor-covenant.org/version/3/0/).
86
+
87
+ Contributor Covenant is stewarded by the Organization for Ethical Source and licensed under CC BY-SA 4.0. To view a copy of this license, visit [https://creativecommons.org/licenses/by-sa/4.0/](https://creativecommons.org/licenses/by-sa/4.0/)
88
+
89
+ For answers to common questions about Contributor Covenant, see the FAQ at [https://www.contributor-covenant.org/faq](https://www.contributor-covenant.org/faq). Translations are provided at [https://www.contributor-covenant.org/translations](https://www.contributor-covenant.org/translations). Additional enforcement and community guideline resources can be found at [https://www.contributor-covenant.org/resources](https://www.contributor-covenant.org/resources). The enforcement ladder was inspired by the work of [Mozilla’s code of conduct team](https://github.com/mozilla/inclusion).
90
+
data/CONTRIBUTING.md CHANGED
@@ -1,15 +1,54 @@
1
1
  # Contributing
2
2
 
3
- Use Ruby 3.3 or newer. Install dependencies and run the suite:
3
+ Outside contributions are welcome. This guide defines the project's [acceptance criteria](#acceptance-criteria).
4
+
5
+ ## Development
6
+
7
+ Use Ruby 3.3 or newer. Install [Betterleaks](https://github.com/betterleaks/betterleaks) with the `betterleaks` command on your PATH.
8
+ Set up dependencies and Git hooks, then run the suite:
4
9
 
5
10
  ```sh
6
- bundle install
11
+ bin/setup
7
12
  bundle exec rake
8
13
  ```
9
14
 
10
- The default check runs tests with fresh SimpleCov coverage and requires every library method's CRAP score to be at most 10.
15
+ Before each commit, hooks run Ruby lint checks and scan staged changes for secrets and whitespace errors.
16
+ Commit messages must use Conventional Commits.
17
+ Before each push, hooks run `bundle exec rake`.
18
+ Put machine-specific hook settings in the ignored `lefthook-local.yml` file.
19
+
20
+ The default check, also available as `bundle exec rake check`, runs RuboCop, Steep, tests with fresh SimpleCov coverage, YARD validation, and dependency audits.
21
+ Every Ruby file must have 100% typed calls.
22
+ Every library method's CRAP score must be at most 10.
11
23
  Use `bundle exec rake test` for tests alone. Open `coverage/index.html` to inspect line coverage.
12
24
 
25
+ Run `bundle exec rake lint` to check Ruby style, formatting, performance, Minitest assertions, and Rake tasks.
26
+ Run `bundle exec rake format` to apply RuboCop's safe corrections, then review the diff.
27
+ RuboCop targets Ruby 3.3 and preserves the project's double-quoted strings and compact hashes.
28
+ YARD and CRAP enforce documentation and complexity requirements; RuboCop's overlapping documentation and metrics checks are disabled.
29
+
30
+ Run `bundle exec rake audit` to check both the development and release lockfiles for known vulnerabilities and insecure gem sources.
31
+ The audit updates RubySec's advisory database over the network and stores it in the ignored `.cache/` directory.
32
+ The default check and pre-push hook therefore require network access.
33
+
34
+ Run `bundle exec rake types` for type checks alone.
35
+ Keep library signatures in `sig/` and signatures for tests and development tools in `sig-development/`.
36
+ The type gate rejects unchecked Ruby files, untyped signatures, and any call that Steep cannot type.
37
+ Add new source locations to `Steepfile` when the gate reports an unchecked file.
38
+
39
+ Document public library methods and data readers with YARD summaries, parameter types, return types, and relevant exceptions.
40
+ Explain private methods when their constraints or side effects need context.
41
+ Keep types consistent with `sig/` and include examples when they clarify usage.
42
+ The Ruby reference is for maintainers and does not extend the [public compatibility policy](VERSIONING.md).
43
+
44
+ Run `bundle exec rake docs` to generate the reference, including private methods, then open `doc/index.html`.
45
+ Run `bundle exec rake docs:check` to reject YARD warnings, undocumented public methods, and readers without explicit documentation and return types.
46
+ Generated files in `doc/` and `.yardoc/` are ignored.
47
+
48
+ After changing dependencies, use Ruby 3.3 and the Bundler version recorded in `Gemfile.lock`.
49
+ Run `bundle exec rake lock` to refresh both lockfiles and their checksums.
50
+ CI installs the release tools from `gemfiles/release/Gemfile.lock` in frozen mode.
51
+
13
52
  Add a regression test for a behavior change. Use temporary projects for filesystem, Git, coverage, and CLI tests.
14
53
  Keep analyzed code out of the tool's process. Preserve the previous snapshot when analysis fails.
15
54
 
@@ -17,3 +56,21 @@ For defect reports, include the Ruby version, command, expected result, actual r
17
56
  Coverage defects also need a matching excerpt from `.resultset.json`. Remove application secrets before sharing examples.
18
57
 
19
58
  Changes use Conventional Commits. Keep code, tests, and documentation for one behavior in a focused commit.
59
+
60
+ ## Acceptance criteria
61
+
62
+ - Keep each change focused on one behavior or correction.
63
+ - Include tests for new functionality and regression tests for behavior changes.
64
+ - Pass `bundle exec rake`, including the method CRAP limit.
65
+ - Update documentation when user-visible behavior changes.
66
+ - Follow the [code of conduct](CODE_OF_CONDUCT.md).
67
+
68
+ ## Submit a change
69
+
70
+ Fork the repository and create a branch from `main`. Commit your change, push the branch to your fork, and open a pull request against `svyatov/crap4ruby:main`.
71
+ Describe the behavior being changed and any effect on existing users. Link the related issue when one exists.
72
+
73
+ ## Decisions and succession
74
+
75
+ Leonid Svyatov (@svyatov) is the sole maintainer and decides which changes to merge and release.
76
+ No succession arrangement exists if he stops maintaining the project.
data/README.md CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  crap4ruby identifies complex, under-tested Ruby methods using the Change Risk Anti-Pattern (CRAP) metric.
4
4
 
5
- - Analyzes Ruby 3.3+ source with Prism without loading application code.
6
- - Reads SimpleCov reports, including legacy formats and multiple test suites.
7
- - Prints method scores and writes the snapshot format used by [crapper](https://github.com/unclebob/crapper).
5
+ - Analyzes Ruby 3.3+ methods without loading application code.
6
+ - Ranks methods using complexity and test coverage, rather than complexity alone.
7
+ - Reads existing SimpleCov reports and merges coverage across test suites.
8
+ - Checks one file, directories, or only files changed in Git.
8
9
 
9
10
  ## Install
10
11
 
@@ -14,12 +15,22 @@ Ruby 3.3 or newer is required.
14
15
  gem install crap4ruby
15
16
  ```
16
17
 
17
- In the project you want to measure:
18
+ In an empty directory, create a sample and analyze it:
19
+
20
+ ```sh
21
+ printf 'def eligible?(age) = age >= 18\n' > sample.rb
22
+ crap4ruby --no-coverage sample.rb
23
+ # Reports eligible? with CC 1; coverage and CRAP are N/A.
24
+ # Writes .metrics/crap.edn.
25
+ ```
26
+
27
+ In an existing project, select the source you want to measure:
18
28
 
19
29
  ```sh
20
30
  crap4ruby --no-coverage
21
31
  crap4ruby
22
32
  crap4ruby --threshold 30
33
+ crap4ruby app/models/user.rb
23
34
  crap4ruby app/models lib/billing
24
35
  ```
25
36
 
@@ -35,7 +46,7 @@ Run these commands from this checkout with Ruby 3.3 or newer:
35
46
  ```sh
36
47
  bundle install
37
48
  gem build crap4ruby.gemspec
38
- gem install ./crap4ruby-0.1.0.gem
49
+ gem install ./crap4ruby-0.2.0.gem
39
50
  ```
40
51
 
41
52
  To use Bundler in the measured project, add this checkout to that project's `Gemfile`:
@@ -48,7 +59,7 @@ Then run `bundle install` and `bundle exec crap4ruby` in that project.
48
59
 
49
60
  ## Set up coverage
50
61
 
51
- [SimpleCov](https://github.com/simplecov-ruby/simplecov) must start before the application loads. Add it to the measured project's test dependencies:
62
+ [SimpleCov](https://github.com/simplecov-ruby/simplecov) must start before the application loads. Add it to the measured project's `Gemfile`:
52
63
 
53
64
  ```ruby
54
65
  gem "simplecov", require: false, group: :test
@@ -85,7 +96,7 @@ crap4ruby does not configure SimpleCov, delete reports, install dependencies, or
85
96
  | `--use-existing-coverage` | Explicitly select the default: read reports without running tests. |
86
97
  | `--coverage-command COMMAND` | Run a shell command that refreshes the selected report. |
87
98
  | `--no-coverage` | Report complexity with unknown coverage and CRAP. |
88
- | `--threshold NUMBER` | Exit 2 when any unrounded score exceeds this finite, nonnegative value. |
99
+ | `--threshold NUMBER` | Check this finite, nonnegative limit; exit 2 for a measured violation, or 1 if an unknown score could exceed it. |
89
100
  | `-h`, `--help` | Print usage. |
90
101
  | `-v`, `--version` | Print the gem version. |
91
102
 
@@ -98,38 +109,49 @@ Discovery includes `.rb` files. It skips hidden directories, test/spec files, an
98
109
  Test filenames are `test_*.rb`, `spec_*.rb`, `*_test.rb`, and `*_spec.rb`.
99
110
  An explicitly named file overrides these exclusions. Directory traversal does not follow symbolic links.
100
111
 
101
- Exit 0 means analysis succeeded. Exit 1 means invalid usage, unreadable or invalid input, a Git failure, or a coverage command failure.
102
- Exit 2 means analysis succeeded but exceeded the threshold. No threshold applies unless you set one.
103
- Every successful analysis replaces the snapshot, including filtered analyses and empty selections.
112
+ Exit 0 means analysis succeeded and any requested threshold was verified. Exit 1 means invalid usage, unreadable or invalid input, a Git failure, a coverage command failure, or an uncertain threshold result.
113
+ Exit 2 means a measured score exceeded the threshold. A measured violation takes precedence over uncertainty. No threshold applies unless you set one.
114
+ Decimal thresholds are compared exactly with the computed scores, before display rounding.
115
+ For unknown scores, the check uses the maximum possible score, `CC^2 + CC`, at zero coverage. If this maximum fits the threshold, the method passes; otherwise, the check returns exit 1 and names the method. Its displayed score remains N/A. This also applies with `--no-coverage`.
116
+ For example, an unknown score at CC 2 passes a threshold of 6, but cannot pass a threshold of 5. Supply fresh, measurable coverage or adjust the limit to resolve uncertainty.
117
+ Every completed analysis replaces the snapshot, including filtered analyses, empty selections, and failed threshold checks.
104
118
 
105
119
  ## Complexity and coverage
106
120
 
107
- The formula uses a coverage fraction between 0 and 1:
121
+ The scoring engine uses this formula, with coverage expressed as a fraction between 0 and 1:
108
122
 
109
123
  ```text
110
124
  CRAP = CC^2 * (1 - coverage)^3 + CC
111
125
  ```
112
126
 
113
- Cyclomatic complexity (CC) starts at 1. Each of these adds 1:
127
+ The formula follows the [original CRAP definition](https://www.artima.com/weblogs/viewpost.jsp?thread=210575). This tool uses line coverage as a proxy for the original definition's basis path coverage. These measurements are not equivalent.
128
+
129
+ Cyclomatic complexity (CC) follows [RuboCop 1.92.1](https://github.com/rubocop/rubocop/blob/aa228eb03094d66aca26664839ca14b10d76f702/lib/rubocop/cop/metrics/cyclomatic_complexity.rb). It starts at 1. Each of these adds 1:
114
130
 
115
131
  - `if`, `elsif`, `unless`, and ternary conditions, including modifier forms.
116
- - `while`, `until`, and `for` loops.
117
- - Each `when` or pattern-matching `in` clause, including a separate increment for a guard condition.
118
- - Each rescue clause and rescue modifier.
119
- - Each `&&`, `||`, `and`, `or`, `&&=`, `||=`, and safe-navigation operation.
132
+ - `while`, `until`, and `for` loops, except post-test `begin ... end while/until` forms.
133
+ - Each `when` or pattern-matching `in` clause. A pattern guard's `if`/`unless` wrapper adds nothing; decisions within its condition still count.
134
+ - Each rescue group, regardless of its number of clauses, and each rescue modifier.
135
+ - Each `&&`, `||`, `and`, `or`, `&&=`, and `||=` operation.
136
+ - Each safe-navigation operation, except repeated use of the same local receiver until its next assignment.
137
+ - Each ordinary block or block argument passed to a [known iteration method](https://github.com/rubocop/rubocop/blob/aa228eb03094d66aca26664839ca14b10d76f702/lib/rubocop/cop/metrics/utils/iterating_block.rb), such as `map`, `each`, or `reduce`.
120
138
 
121
139
  A `when` clause adds 1 regardless of its number of alternatives. `else` and `ensure` add nothing.
122
140
  Decisions inside ordinary blocks and lambdas belong to their enclosing method.
123
- Decisions in parameter defaults also count; coverage still measures only the body.
124
- Nested methods are separate entries. Their bodies and nested class/module bodies do not increase the enclosing method's score.
125
- Calling `each`, `map`, or another iterator does not itself add complexity.
141
+ The method's own parameter defaults do not count. Nested definitions and their defaults, including nested class/module bodies, contribute decisions to the enclosing body, as in RuboCop.
142
+ The safe-navigation discount follows local variable names across that entire body, including nested scopes, in RuboCop's AST traversal order. Assignments reset it; parameters, pattern bindings, and named regexp captures do not.
143
+ RuboCop 1.92.1 does not add an iteration point for numbered or implicit-`it` blocks. This tool preserves that version's behavior. Iterator calls without a block add nothing.
144
+ Nested methods are also separate entries. Definitions inside parameter defaults and superclass expressions are discovered too.
126
145
 
127
146
  Coverage is the fraction of executable method-body lines with positive hit counts.
128
- Separate `def` headers are excluded: loading a definition does not prove that tests called its method.
147
+ Declaration lines and nested method/class/module bodies are excluded from coverage. Loading a definition does not prove that tests called its method.
148
+ When the body shares a line with its declaration, coverage and CRAP are N/A, even if that line has hits. This includes ordinary inline methods, one-line endless methods, and inline `define_method` blocks. Put the body on separate lines to obtain measurable coverage.
149
+ Coverage is also N/A when a selected body line contains trailing code after the definition, including enclosing delimiters that cannot be separated from that line's hit count. Put the closing delimiter on its own line. Comments and empty statement separators do not trigger this check.
150
+ Executable heredoc interpolation lines are included, even when the heredoc is the method's final expression.
129
151
  Null lines do not enter the denominator. Multiple suites are merged by covered-line union, without applying an age cutoff.
130
152
  Coverage paths must match absolute source paths or paths relative to the project root; unrelated checkout prefixes are not guessed.
131
153
 
132
- A method absent from a valid report receives 0% coverage. A body with no measurable lines receives N/A.
154
+ A method with unambiguous body lines in a file absent from a valid report receives 0% coverage. A reported body with no measurable lines receives N/A.
133
155
  Missing or malformed reports stop analysis unless `--no-coverage` is explicit.
134
156
  Reports are accepted as supplied; generate fresh coverage after changing source files.
135
157
 
@@ -153,12 +175,15 @@ A filtered run writes only its selected methods. Reopened classes and repeated d
153
175
 
154
176
  ## Limits
155
177
 
156
- Analysis covers explicit method definitions, including private and endless methods.
157
- It does not execute Ruby or resolve runtime metaprogramming, `define_method`, aliases, generated accessors, or dynamic singleton receivers.
178
+ Analysis covers explicit method definitions, including private and endless methods, and bare `define_method` calls with one literal symbol/string name and a block.
179
+ Literal `define_method` names must be representable in UTF-8; unsupported names produce an error without replacing the previous snapshot.
180
+ It does not execute Ruby or resolve other runtime metaprogramming, dynamic `define_method` names, calls with explicit receivers, aliases, generated accessors, or dynamic singleton receivers.
158
181
  Namespaces derived from expressions, such as `factory::Thing`, are skipped with their contents.
159
- Line coverage cannot distinguish expressions or methods sharing a source line, or separate evaluation of default parameters.
182
+ Line coverage cannot distinguish expressions sharing a source line or separate evaluation of default parameters.
160
183
  Branch coverage is not used. Static namespace names do not reflect runtime constant reassignment.
161
184
 
185
+ These counting and threshold rules change results from 0.1.x and require a minor release under the versioning policy. Regenerate coverage and snapshots and review existing limits when upgrading.
186
+
162
187
  ## Development
163
188
 
164
189
  ```sh
@@ -178,11 +203,14 @@ The implementation is tested on CRuby 3.3, 3.4, and 4.0. Other Ruby implementati
178
203
 
179
204
  ## Releases
180
205
 
206
+ The [versioning policy](VERSIONING.md) defines the public interface, version changes, and deprecation rules.
207
+
181
208
  Signed `v*` tags trigger the release workflow. The tag must match `Crap4Ruby::VERSION`.
182
209
  Tests and the CRAP limit run before the gem is built. Publication requires approval in GitHub's `release` environment.
183
210
  RubyGems trusted publishing authenticates the workflow. Sigstore signs the built gem, and RubyGems stores its attestation.
184
211
 
185
212
  Report defects or ask questions through [GitHub issues](https://github.com/svyatov/crap4ruby/issues).
213
+ Leonid Svyatov maintains this project. Security fixes target the latest release; see the [security policy](SECURITY.md).
186
214
 
187
215
  ## License
188
216
 
data/SECURITY.md ADDED
@@ -0,0 +1,15 @@
1
+ # Security policy
2
+
3
+ ## Report a vulnerability
4
+
5
+ Send private reports to [leonid@svyatov.com](mailto:leonid@svyatov.com). Do not disclose an unpatched vulnerability in a public issue.
6
+
7
+ Include the affected crap4ruby and Ruby versions, a minimal reproduction, and the expected security impact.
8
+ Remove credentials and private application data from examples.
9
+
10
+ Leonid Svyatov monitors this address and will respond within 14 days.
11
+ The response will confirm receipt and describe the next investigation step; it does not promise a fix within that period.
12
+
13
+ ## Supported versions
14
+
15
+ Security fixes target the latest release. Reproduce the issue with that release when possible, but report suspected vulnerabilities in older releases too.
data/VERSIONING.md ADDED
@@ -0,0 +1,30 @@
1
+ # Versioning policy
2
+
3
+ crap4ruby uses [Semantic Versioning 2.0.0](https://semver.org/spec/v2.0.0.html) for its documented public interface.
4
+
5
+ ## Public interface
6
+
7
+ The compatibility policy covers:
8
+
9
+ - Documented CLI options, positional file selection, and exit codes.
10
+ - Documented complexity, coverage, and CRAP scoring semantics.
11
+ - The `.metrics/crap.edn` schema, including field meanings and value types.
12
+ - The minimum supported Ruby version.
13
+
14
+ Terminal table spacing, diagnostic wording, and Ruby classes are implementation details.
15
+ YARD documents the Ruby interfaces for maintainers; those interfaces are outside this compatibility policy.
16
+ Use the CLI and documented snapshot format when integrating with other tools.
17
+
18
+ ## Version changes
19
+
20
+ During initial development (`0.y.z`), incompatible public changes increase MINOR. Compatible additions, fixes, and deprecations increase PATCH.
21
+ Starting with `1.0.0`, incompatible public changes increase MAJOR, compatible additions and deprecations increase MINOR, and compatible fixes increase PATCH.
22
+
23
+ Each release tag, gem version, and changelog entry must identify the same version. Published versions are never replaced.
24
+ Documentation and CI maintenance alone do not require a release.
25
+
26
+ ## Deprecation
27
+
28
+ Deprecate a public interface in a release before removing it in a later release.
29
+ The notice must name its replacement or migration path and the earliest removal version, using a CLI diagnostic where applicable.
30
+ Removal requires a later MINOR release during initial development or a later MAJOR release after `1.0.0`.