philiprehberger-differ 0.3.0 → 0.5.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 +17 -0
- data/README.md +20 -0
- data/lib/philiprehberger/differ/changeset.rb +40 -0
- data/lib/philiprehberger/differ/version.rb +1 -1
- data/lib/philiprehberger/differ.rb +25 -0
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f6f6c2e127be0912f009924625aa0ef3babcc21855bb9a6f0af744f6c6dff179
|
|
4
|
+
data.tar.gz: c2c99a65ed6ad72b399f52c99a3ac5866ca0c7705917c395124281c570869d31
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 9e3c7e4dcfabefe06c2348f4e13a1fa3023234ac3e57c2e8c3ac51437027944c906bfe3bc721e57d259087381bba09ee734181b80a83ce69872e6be814d1e041
|
|
7
|
+
data.tar.gz: 6796e028cb9a84341db6fc7952e79c692bbcd8d87dbb1af116da04fdd883785db8c63a0e80909940355aae65f993b31437eb69aaa2ab27805f372ed69841ea4c
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,23 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.5.0] - 2026-04-21
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `Philiprehberger::Differ.stats` — structured counts of added/removed/changed paths in a changeset
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
- `bug_report.yml` — require Ruby version; add Gem version input per guide
|
|
17
|
+
|
|
18
|
+
## [0.4.0] - 2026-04-10
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
- `Changeset` now includes `Enumerable` for direct iteration over changes
|
|
22
|
+
- `Changeset#count` returning the number of changes
|
|
23
|
+
- `Changeset#paths` returning all changed paths
|
|
24
|
+
- `Changeset#include?(path)` to check if a specific path was changed
|
|
25
|
+
- `Changeset#summary` returning `{ added:, removed:, changed: }` counts
|
|
26
|
+
|
|
10
27
|
## [0.3.0] - 2026-04-01
|
|
11
28
|
|
|
12
29
|
### Added
|
data/README.md
CHANGED
|
@@ -139,6 +139,20 @@ result[:conflicts] # => [{ path:, theirs:, ours: }]
|
|
|
139
139
|
Philiprehberger::Differ.breaking_changes?(changeset) # => true/false
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### Change Statistics
|
|
143
|
+
|
|
144
|
+
Get a structured count summary of a changeset:
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
changeset = Philiprehberger::Differ.diff(
|
|
148
|
+
{ name: 'Alice', age: 30, id: 1 },
|
|
149
|
+
{ name: 'Bob', email: 'bob@example.com' }
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
Philiprehberger::Differ.stats(changeset)
|
|
153
|
+
# => { added: 1, removed: 2, changed: 1, total: 4, paths: 4 }
|
|
154
|
+
```
|
|
155
|
+
|
|
142
156
|
## API
|
|
143
157
|
|
|
144
158
|
### `Philiprehberger::Differ.diff(old_val, new_val, ignore: [], array_key: nil)`
|
|
@@ -154,6 +168,11 @@ Returns a Float between 0.0 (completely different) and 1.0 (identical).
|
|
|
154
168
|
| Method | Description |
|
|
155
169
|
|---|---|
|
|
156
170
|
| `changed?` | Returns `true` if any differences exist |
|
|
171
|
+
| `count` | Number of changes |
|
|
172
|
+
| `paths` | Array of all changed paths |
|
|
173
|
+
| `include?(path)` | Check if a specific path was changed |
|
|
174
|
+
| `summary` | `{ added:, removed:, changed: }` counts |
|
|
175
|
+
| `each` | Iterate over changes (includes `Enumerable`) |
|
|
157
176
|
| `changes` | All `Change` objects |
|
|
158
177
|
| `added` | Changes where `type == :added` |
|
|
159
178
|
| `removed` | Changes where `type == :removed` |
|
|
@@ -169,6 +188,7 @@ Returns a Float between 0.0 (completely different) and 1.0 (identical).
|
|
|
169
188
|
| `Differ.subset(changeset, path)` | Filter changes to a specific path prefix |
|
|
170
189
|
| `Differ.merge(base, theirs, ours)` | Three-way merge with conflict detection |
|
|
171
190
|
| `Differ.breaking_changes?(changeset)` | Detect removals and type changes |
|
|
191
|
+
| `.stats(changeset)` | Count summary of a changeset |
|
|
172
192
|
|
|
173
193
|
### `Change`
|
|
174
194
|
|
|
@@ -3,12 +3,52 @@
|
|
|
3
3
|
module Philiprehberger
|
|
4
4
|
module Differ
|
|
5
5
|
class Changeset
|
|
6
|
+
include Enumerable
|
|
7
|
+
|
|
6
8
|
attr_reader :changes
|
|
7
9
|
|
|
8
10
|
def initialize(changes = [])
|
|
9
11
|
@changes = changes
|
|
10
12
|
end
|
|
11
13
|
|
|
14
|
+
# Iterate over each Change.
|
|
15
|
+
#
|
|
16
|
+
# @yield [Change] each change
|
|
17
|
+
# @return [Enumerator] if no block given
|
|
18
|
+
def each(&)
|
|
19
|
+
@changes.each(&)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Number of changes.
|
|
23
|
+
#
|
|
24
|
+
# @return [Integer]
|
|
25
|
+
def count
|
|
26
|
+
@changes.length
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Array of all changed paths.
|
|
30
|
+
#
|
|
31
|
+
# @return [Array<String>]
|
|
32
|
+
def paths
|
|
33
|
+
@changes.map(&:path)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Check if a specific path was changed.
|
|
37
|
+
#
|
|
38
|
+
# @param path [String, Symbol] the path to check
|
|
39
|
+
# @return [Boolean]
|
|
40
|
+
def include?(path)
|
|
41
|
+
path_s = path.to_s
|
|
42
|
+
@changes.any? { |c| c.path.to_s == path_s }
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# Summary counts by change type.
|
|
46
|
+
#
|
|
47
|
+
# @return [Hash{Symbol => Integer}] { added:, removed:, changed: }
|
|
48
|
+
def summary
|
|
49
|
+
{ added: added.length, removed: removed.length, changed: changed.length }
|
|
50
|
+
end
|
|
51
|
+
|
|
12
52
|
def changed?
|
|
13
53
|
!@changes.empty?
|
|
14
54
|
end
|
|
@@ -56,6 +56,31 @@ module Philiprehberger
|
|
|
56
56
|
{ merged: merged, conflicts: conflicts }
|
|
57
57
|
end
|
|
58
58
|
|
|
59
|
+
# Structured count summary of a changeset.
|
|
60
|
+
#
|
|
61
|
+
# Returns a hash of integer counts for the added, removed, and changed
|
|
62
|
+
# entries in the changeset, along with a running total and the number of
|
|
63
|
+
# unique paths. This method is read-only and never mutates the changeset.
|
|
64
|
+
#
|
|
65
|
+
# @param changeset [Changeset] the changeset to summarize
|
|
66
|
+
# @return [Hash{Symbol => Integer}] counts by kind — `{ added:, removed:, changed:, total:, paths: }`
|
|
67
|
+
# @raise [ArgumentError] if `changeset` is not a {Changeset}
|
|
68
|
+
def self.stats(changeset)
|
|
69
|
+
raise ArgumentError, 'changeset must be a Philiprehberger::Differ::Changeset' unless changeset.is_a?(Changeset)
|
|
70
|
+
|
|
71
|
+
added = changeset.added.length
|
|
72
|
+
removed = changeset.removed.length
|
|
73
|
+
changed = changeset.changed.length
|
|
74
|
+
|
|
75
|
+
{
|
|
76
|
+
added: added,
|
|
77
|
+
removed: removed,
|
|
78
|
+
changed: changed,
|
|
79
|
+
total: added + removed + changed,
|
|
80
|
+
paths: changeset.paths.length
|
|
81
|
+
}
|
|
82
|
+
end
|
|
83
|
+
|
|
59
84
|
# Detect if a changeset contains breaking changes (removals or type changes)
|
|
60
85
|
#
|
|
61
86
|
# @param changeset [Changeset] the changeset to check
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: philiprehberger-differ
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Philip Rehberger
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2026-04-
|
|
11
|
+
date: 2026-04-21 00:00:00.000000000 Z
|
|
12
12
|
dependencies: []
|
|
13
13
|
description: A Ruby library for deep structural diffing of hashes, arrays, and nested
|
|
14
14
|
objects with apply/revert, JSON Patch output, similarity scoring, and configurable
|