even_better_specs 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 +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +8 -0
- data/exe/even_better_specs +1 -2
- data/lib/even_better_specs/cli.rb +47 -0
- data/lib/even_better_specs/diff.rb +70 -0
- data/lib/even_better_specs/version.rb +1 -1
- data/lib/even_better_specs.rb +2 -0
- metadata +4 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: d351b776d1b0ae7a8ade491c02fc817eb10fa9609bced214c22f3eadbf98b44a
|
|
4
|
+
data.tar.gz: 23429a0a67b2ff0213e79d951821c98132d8d53dfe8467b22a44036215c61451
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: fcb236582d722386c56855e48e7300c51fdf4c513ab6b19d1dd5275add4f11740587e77c01ad1f6282396dbd659728340709d62a76314bcc48877814391f3def
|
|
7
|
+
data.tar.gz: a87a96aa03ffcf87e0fe6426e5b750db360140f33f0b77b1b9247e53128f7b61956c101d32b4401cffb6dbc8a48a5fa7000508781187d25d7e94c2540d3583ba
|
data/CHANGELOG.md
CHANGED
|
@@ -6,6 +6,12 @@ and this project follows [Semantic Versioning](https://semver.org/).
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.2.0]
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `--diff[=BASE]` option: report only offenses on lines changed since a git revision (default `HEAD`).
|
|
14
|
+
|
|
9
15
|
## [0.1.0] - 2026-10-30
|
|
10
16
|
|
|
11
17
|
### Added
|
data/README.md
CHANGED
|
@@ -12,11 +12,19 @@ gem install even_better_specs
|
|
|
12
12
|
even_better_specs # checks every *_spec.rb
|
|
13
13
|
even_better_specs spec/models
|
|
14
14
|
even_better_specs -a # autocorrect what can be autocorrected
|
|
15
|
+
even_better_specs --diff # only lines changed since HEAD, plus untracked specs
|
|
16
|
+
even_better_specs --diff=develop # only lines changed since develop (e.g. a whole branch)
|
|
15
17
|
```
|
|
16
18
|
|
|
17
19
|
Do not use `bundle exec`, because the gem is not in the Gemfile. The CLI accepts all `rubocop` options.
|
|
18
20
|
It runs with its own config only, so the project's `.rubocop.yml` (if any) is ignored.
|
|
19
21
|
|
|
22
|
+
`--diff[=BASE]` reports only offenses that start on a line added or changed since `BASE` (default `HEAD`),
|
|
23
|
+
so you are judged on what you wrote, not on the file's legacy. Without paths it inspects the changed spec
|
|
24
|
+
files; with paths it inspects those and still filters by the diff. The exit code counts only the reported
|
|
25
|
+
offenses. An offense that starts on an unchanged line (e.g. an existing `describe` around your new code) is
|
|
26
|
+
not reported. It cannot be combined with autocorrect, which would rewrite lines outside the diff.
|
|
27
|
+
|
|
20
28
|
## Rules
|
|
21
29
|
|
|
22
30
|
| Guideline | Cop |
|
data/exe/even_better_specs
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "pathname"
|
|
4
|
+
|
|
5
|
+
module EvenBetterSpecs
|
|
6
|
+
# Runs RuboCop with the gem's config, plus the `--diff[=BASE]` option.
|
|
7
|
+
module CLI
|
|
8
|
+
AUTOCORRECT_FLAGS = %w[-a -A -x --autocorrect --autocorrect-all --fix-layout].freeze
|
|
9
|
+
|
|
10
|
+
def self.run(argv)
|
|
11
|
+
args = argv.dup
|
|
12
|
+
flag = args.find { |arg| arg == "--diff" || arg.start_with?("--diff=") }
|
|
13
|
+
return rubocop(args) unless flag
|
|
14
|
+
|
|
15
|
+
args.delete(flag)
|
|
16
|
+
if args.intersect?(AUTOCORRECT_FLAGS)
|
|
17
|
+
warn "--diff cannot be combined with autocorrect: it would rewrite lines outside the diff."
|
|
18
|
+
return 2
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
base = flag.include?("=") ? flag.split("=", 2).last : "HEAD"
|
|
22
|
+
Diff.changed_lines = Diff.compute(base)
|
|
23
|
+
if Diff.changed_lines.empty?
|
|
24
|
+
warn "No changes since #{base}."
|
|
25
|
+
return 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Without explicit paths, inspect only the changed files that the config's Include matches.
|
|
29
|
+
if args.none? { |arg| File.exist?(arg) }
|
|
30
|
+
cwd = Pathname.new(File.realpath(Dir.pwd))
|
|
31
|
+
files = Diff.changed_lines.keys.map { |file| Pathname.new(file).relative_path_from(cwd).to_s }
|
|
32
|
+
args += ["--only-recognized-file-types", *files]
|
|
33
|
+
end
|
|
34
|
+
rubocop(args)
|
|
35
|
+
rescue ArgumentError => e
|
|
36
|
+
warn e.message
|
|
37
|
+
2
|
|
38
|
+
ensure
|
|
39
|
+
Diff.changed_lines = nil
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def self.rubocop(args)
|
|
43
|
+
# --config makes RuboCop ignore the project's own .rubocop.yml (if any).
|
|
44
|
+
RuboCop::CLI.new.run(["--config", CONFIG, *args])
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "open3"
|
|
4
|
+
|
|
5
|
+
module EvenBetterSpecs
|
|
6
|
+
# Limits a run to the lines added or changed since a git revision (`--diff`).
|
|
7
|
+
module Diff
|
|
8
|
+
HUNK = /\A@@ -\S+ \+(\d+)(?:,(\d+))? @@/
|
|
9
|
+
WHOLE_FILE = [1..].freeze
|
|
10
|
+
|
|
11
|
+
class << self
|
|
12
|
+
# { absolute path => [line ranges] } while a `--diff` run is active, nil otherwise.
|
|
13
|
+
attr_accessor :changed_lines
|
|
14
|
+
|
|
15
|
+
# Changed line ranges per file since `base`. Untracked files count as changed in full,
|
|
16
|
+
# like RuboCop's `--changed`.
|
|
17
|
+
def compute(base)
|
|
18
|
+
root = git("rev-parse", "--show-toplevel").chomp
|
|
19
|
+
lines = parse(git("diff", "-U0", "--no-color", "--no-ext-diff", "--src-prefix=a/", "--dst-prefix=b/", base), root)
|
|
20
|
+
git("ls-files", "--others", "--exclude-standard", "--full-name", chdir: root).each_line do |path|
|
|
21
|
+
lines[File.join(root, path.chomp)] = WHOLE_FILE
|
|
22
|
+
end
|
|
23
|
+
lines
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def parse(diff, root)
|
|
27
|
+
file = nil
|
|
28
|
+
diff.each_line.with_object({}) do |line, lines|
|
|
29
|
+
if line.start_with?("+++ ")
|
|
30
|
+
path = line.chomp.chomp("\t").delete_prefix("+++ ")
|
|
31
|
+
file = path == "/dev/null" ? nil : File.join(root, path.delete_prefix("b/"))
|
|
32
|
+
elsif file && (hunk = HUNK.match(line))
|
|
33
|
+
start = hunk[1].to_i
|
|
34
|
+
count = (hunk[2] || 1).to_i
|
|
35
|
+
(lines[file] ||= []) << (start...start + count) if count.positive?
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def changed?(file, line)
|
|
41
|
+
# git reports real paths; RuboCop may hold a symlinked one (e.g. /tmp on macOS).
|
|
42
|
+
changed_lines.fetch(File.realpath(file), []).any? { |range| range.cover?(line) }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
def git(*args, chdir: Dir.pwd)
|
|
48
|
+
output, error, status = Open3.capture3("git", *args, chdir: chdir)
|
|
49
|
+
raise ArgumentError, "git #{args.first} failed: #{error.strip}" unless status.success?
|
|
50
|
+
|
|
51
|
+
output
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Drops offenses outside the changed lines. Hooked after RuboCop's result cache, so the cache
|
|
56
|
+
# keeps full results, and before reporting, so every formatter and the exit code agree.
|
|
57
|
+
module RunnerFilter
|
|
58
|
+
private
|
|
59
|
+
|
|
60
|
+
def process_file(file)
|
|
61
|
+
offenses = super
|
|
62
|
+
return offenses unless Diff.changed_lines
|
|
63
|
+
|
|
64
|
+
offenses.select { |offense| Diff.changed?(file, offense.line) }.freeze
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
RuboCop::Runner.prepend(EvenBetterSpecs::Diff::RunnerFilter)
|
data/lib/even_better_specs.rb
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
require "rubocop"
|
|
4
4
|
require "rubocop-rspec"
|
|
5
5
|
require_relative "even_better_specs/version"
|
|
6
|
+
require_relative "even_better_specs/diff"
|
|
7
|
+
require_relative "even_better_specs/cli"
|
|
6
8
|
|
|
7
9
|
module EvenBetterSpecs
|
|
8
10
|
CONFIG = File.expand_path("../config/default.yml", __dir__)
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: even_better_specs
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.2.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Glauco Custodio
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-
|
|
11
|
+
date: 2026-11-03 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rubocop
|
|
@@ -52,6 +52,8 @@ files:
|
|
|
52
52
|
- config/default.yml
|
|
53
53
|
- exe/even_better_specs
|
|
54
54
|
- lib/even_better_specs.rb
|
|
55
|
+
- lib/even_better_specs/cli.rb
|
|
56
|
+
- lib/even_better_specs/diff.rb
|
|
55
57
|
- lib/even_better_specs/version.rb
|
|
56
58
|
- lib/rubocop/cop/even_better_specs/all_possible_cases.rb
|
|
57
59
|
- lib/rubocop/cop/even_better_specs/create_only_needed_data.rb
|