check_please 0.2.0 → 0.3.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/Gemfile.lock +1 -1
- data/README.md +24 -31
- data/Rakefile +16 -1
- data/{bin → exe}/check_please +1 -1
- data/lib/check_please.rb +14 -10
- data/lib/check_please/cli.rb +19 -3
- data/lib/check_please/cli/flag.rb +1 -0
- data/lib/check_please/comparison.rb +9 -3
- data/lib/check_please/diff.rb +2 -10
- data/lib/check_please/diffs.rb +8 -1
- data/lib/check_please/path.rb +4 -0
- data/lib/check_please/printers/json.rb +0 -2
- data/lib/check_please/printers/table_print.rb +18 -5
- data/lib/check_please/version.rb +1 -1
- metadata +5 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 889dea37051513ced2de5a716db086a1ad1793af6e095704be6cc39a0fc64822
|
4
|
+
data.tar.gz: 2a21aeeb5a686b46a4657dd2d50163b07e0e6e2f1effb8fdbe652bbc835f5c02
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 72b705dc899a841fadce3a9d67c538012d83d7e9fce7e69d8b7da4d6795eb84ac6eb4c8feedc8f721b07c0894c4a87079584081350ade4a3a184aa7ba7fad0d2
|
7
|
+
data.tar.gz: c13e453a1032c1ad9b57e7e20c272851044dce1c80ca63bde5b173e05f547b2f43c39c3910cd1decebbe4da28eb2e71ba2fbeb4c23f145c40176bf3391292431
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# check_please
|
2
2
|
|
3
|
-
Check for differences between two JSON
|
3
|
+
Check for differences between two JSON documents, YAML documents, or Ruby data
|
4
|
+
structures parsed from either of those.
|
4
5
|
|
5
6
|
## Installation
|
6
7
|
|
@@ -18,11 +19,10 @@ Or install it yourself as:
|
|
18
19
|
|
19
20
|
$ gem install check_please
|
20
21
|
|
21
|
-
##
|
22
|
-
|
23
|
-
### Terminology
|
22
|
+
## Terminology
|
24
23
|
|
25
|
-
|
24
|
+
I know, you just want to see how to use this thing. Feel free to scroll down,
|
25
|
+
but be aware that CheckPlease uses a few words in a jargony way:
|
26
26
|
|
27
27
|
* **Reference** is always used to refer to the "target" or "source of truth."
|
28
28
|
We assume you're comparing two things because you want one of them to be like
|
@@ -35,7 +35,9 @@ CheckPlease uses a few words in a jargony way:
|
|
35
35
|
**reference** and the **candidate**. More on this in "Understanding the Output",
|
36
36
|
below.
|
37
37
|
|
38
|
-
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
### From the Terminal
|
39
41
|
|
40
42
|
Use the `bin/check_please` executable. (To get started, run it with the '-h' flag.)
|
41
43
|
|
@@ -46,19 +48,16 @@ of giving it a second filename as the argument. (This is especially useful if
|
|
46
48
|
you're copying an XHR response out of a web browser's dev tools and have a tool
|
47
49
|
like MacOS's `pbpaste` utility.)
|
48
50
|
|
49
|
-
### RSpec
|
51
|
+
### From RSpec
|
50
52
|
|
51
53
|
See [check_please_rspec_matcher](https://github.com/RealGeeks/check_please_rspec_matcher).
|
52
54
|
|
53
|
-
### From
|
54
|
-
|
55
|
-
Create two JSON strings and pass them to `CheckPlease.render_diff`. You'll get
|
56
|
-
back a third string containing a nicely formatted report of all the differences
|
57
|
-
CheckPlease found in the two JSON strings. (See also: [./usage_examples.rb](usage_examples.rb).)
|
55
|
+
### From Ruby
|
58
56
|
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
Create two strings, each containing a JSON or YAML document, and pass them to
|
58
|
+
`CheckPlease.render_diff`. You'll get back a third string containing a report
|
59
|
+
of all the differences CheckPlease found in the two JSON strings. (See also:
|
60
|
+
[./usage_examples.rb](usage_examples.rb).)
|
62
61
|
|
63
62
|
### Understanding the Output
|
64
63
|
|
@@ -70,9 +69,7 @@ tool because you want a human-friendly summary of all the places that your
|
|
70
69
|
**candidate** fell short.
|
71
70
|
|
72
71
|
When CheckPlease compares your two samples, it generates a list of diffs to
|
73
|
-
describe any discrepancies it encounters.
|
74
|
-
tabular format, but if you want to incorporate this into another toolchain,
|
75
|
-
CheckPlease can also print these diffs as JSON to facilitate parsing.)
|
72
|
+
describe any discrepancies it encounters.
|
76
73
|
|
77
74
|
An example would probably help here.
|
78
75
|
|
@@ -124,23 +121,22 @@ CheckPlease defines:
|
|
124
121
|
* **type_mismatch** means that both the **reference** and the **candidate** had
|
125
122
|
a value at the given path, but one value was an Array or a Hash and the other
|
126
123
|
was not. **When CheckPlease encounters a type mismatch, it does not compare
|
127
|
-
anything "below" the given path.**
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
Also, the way these get displayed is likely to change.)_
|
124
|
+
anything "below" the given path.** _(Technical note: CheckPlease uses a
|
125
|
+
"recursive descent" strategy to traverse the **reference** data structure,
|
126
|
+
and it stops when it encounters a type mismatch in order to avoid producing a
|
127
|
+
lot of "garbage" diff output.)_
|
132
128
|
* **mismatch** means that both the **reference** and the **candidate** had a
|
133
129
|
value at the given path, and neither value was an Array or a Hash.
|
134
|
-
*
|
135
|
-
|
136
|
-
*
|
130
|
+
* **extra** means that, inside an Array or a Hash, the **candidate** contained
|
131
|
+
values that were not found in the **reference**.
|
132
|
+
* **missing** is the opposite of **extra**: inside an Array or a Hash, the
|
137
133
|
**reference** contained values that were not found in the **candidate**.
|
138
134
|
|
139
135
|
#### Paths
|
140
136
|
|
141
|
-
The second column contains a path expression. This is extremely
|
137
|
+
The second column contains a path expression. This is extremely lo-fi:
|
142
138
|
|
143
|
-
* The
|
139
|
+
* The root of the data structure is defined as "/".
|
144
140
|
* If an element in the data structure is an array, its child elements will have
|
145
141
|
a **one-based** index appended to their parent's path.
|
146
142
|
* If an element in the data structure is an object ("Hash" in Ruby), the key
|
@@ -163,10 +159,7 @@ print diffs as JSON to facilitate parsing. In Ruby, pass `format: :json` to
|
|
163
159
|
## TODO
|
164
160
|
|
165
161
|
* command line flags for :allthethings:!
|
166
|
-
* --fail-fast
|
167
|
-
* limit to first N
|
168
162
|
* sort by path?
|
169
|
-
* max depth (for iterative refinement?)
|
170
163
|
* detect timestamps and compare after parsing?
|
171
164
|
* ignore sub-second precision (option / CLI flag)?
|
172
165
|
* possibly support plugins for other folks to add custom coercions?
|
data/Rakefile
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
require "rspec/core/rake_task"
|
3
3
|
|
4
|
-
|
4
|
+
namespace :spec do
|
5
|
+
RSpec::Core::RakeTask.new(:all)
|
6
|
+
|
7
|
+
RSpec::Core::RakeTask.new(:not_cli) do |t|
|
8
|
+
t.rspec_opts = "--tag ~cli"
|
9
|
+
end
|
10
|
+
task fast: :not_cli
|
11
|
+
|
12
|
+
# These are much slower than the rest, since they use Kernel#`
|
13
|
+
RSpec::Core::RakeTask.new(:cli) do |t|
|
14
|
+
t.rspec_opts = "--tag cli"
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# By default, `rake spec` should run fast specs first, then cli if those all pass
|
19
|
+
task :spec => [ "spec:not_cli", "spec:cli" ]
|
5
20
|
|
6
21
|
task :default => :spec
|
data/{bin → exe}/check_please
RENAMED
data/lib/check_please.rb
CHANGED
@@ -7,17 +7,20 @@ require_relative "check_please/diffs"
|
|
7
7
|
require_relative "check_please/printers"
|
8
8
|
require_relative "check_please/cli"
|
9
9
|
|
10
|
+
require 'yaml'
|
11
|
+
require 'json'
|
12
|
+
|
10
13
|
module CheckPlease
|
11
14
|
ELEVATOR_PITCH = "Tool for parsing and diffing two JSON documents."
|
12
15
|
|
13
|
-
def self.diff(reference, candidate)
|
16
|
+
def self.diff(reference, candidate, options = {})
|
14
17
|
reference = maybe_parse(reference)
|
15
18
|
candidate = maybe_parse(candidate)
|
16
|
-
Comparison.perform(reference, candidate)
|
19
|
+
Comparison.perform(reference, candidate, options)
|
17
20
|
end
|
18
21
|
|
19
22
|
def self.render_diff(reference, candidate, options = {})
|
20
|
-
diffs = diff(reference, candidate)
|
23
|
+
diffs = diff(reference, candidate, options)
|
21
24
|
Printers.render(diffs, options)
|
22
25
|
end
|
23
26
|
|
@@ -25,16 +28,17 @@ module CheckPlease
|
|
25
28
|
private
|
26
29
|
|
27
30
|
# Maybe you gave us JSON strings, maybe you gave us Ruby objects.
|
28
|
-
# We just don't know!
|
29
|
-
|
31
|
+
# Heck, maybe you even gave us some YAML! We just don't know!
|
32
|
+
# That's what makes it so exciting!
|
33
|
+
def maybe_parse(document)
|
30
34
|
|
31
|
-
case
|
32
|
-
when String ;
|
33
|
-
else ;
|
35
|
+
case document
|
36
|
+
when String ; return YAML.load(document) # don't worry, if this raises we'll assume you've already parsed it
|
37
|
+
else ; return document
|
34
38
|
end
|
35
39
|
|
36
|
-
rescue JSON::ParserError
|
37
|
-
return
|
40
|
+
rescue JSON::ParserError, Psych::SyntaxError
|
41
|
+
return document
|
38
42
|
end
|
39
43
|
end
|
40
44
|
end
|
data/lib/check_please/cli.rb
CHANGED
@@ -13,17 +13,33 @@ module CheckPlease
|
|
13
13
|
|
14
14
|
|
15
15
|
FLAGS = []
|
16
|
-
def self.flag(
|
17
|
-
flag = Flag.new(
|
16
|
+
def self.flag(long:, short: nil, &block)
|
17
|
+
flag = Flag.new(short, long, &block)
|
18
18
|
FLAGS << flag
|
19
19
|
end
|
20
20
|
|
21
21
|
##### Define CLI flags here #####
|
22
22
|
|
23
|
-
flag "-f FORMAT", "--format FORMAT" do |f|
|
23
|
+
flag short: "-f FORMAT", long: "--format FORMAT" do |f|
|
24
24
|
f.desc = "format in which to present diffs (available options: [#{CheckPlease::Printers::FORMATS.join(", ")}])"
|
25
25
|
f.set_key :format, :to_sym
|
26
26
|
end
|
27
|
+
|
28
|
+
flag short: "-n MAX_DIFFS", long: "--max-diffs MAX_DIFFS" do |f|
|
29
|
+
f.desc = "Stop after encountering a specified number of diffs"
|
30
|
+
f.set_key :max_diffs, :to_i
|
31
|
+
end
|
32
|
+
|
33
|
+
flag long: "--fail-fast" do |f|
|
34
|
+
f.desc = "Stop after encountering the very first diff"
|
35
|
+
f.set_key(:max_diffs) { 1 }
|
36
|
+
end
|
37
|
+
|
38
|
+
flag short: "-d MAX_DEPTH", long: "--max-depth MAX_DEPTH" do |f|
|
39
|
+
f.desc = "Limit the number of levels to descend when comparing documents (NOTE: root has depth=1)"
|
40
|
+
f.set_key :max_depth, :to_i
|
41
|
+
end
|
42
|
+
|
27
43
|
end
|
28
44
|
|
29
45
|
end
|
@@ -3,16 +3,22 @@ module CheckPlease
|
|
3
3
|
module Comparison
|
4
4
|
extend self
|
5
5
|
|
6
|
-
def perform(reference, candidate)
|
6
|
+
def perform(reference, candidate, options = {})
|
7
7
|
root = CheckPlease::Path.new
|
8
|
-
diffs = Diffs.new
|
9
|
-
|
8
|
+
diffs = Diffs.new(options: options)
|
9
|
+
catch(:max_diffs_reached) do
|
10
|
+
compare reference, candidate, root, diffs
|
11
|
+
end
|
10
12
|
diffs
|
11
13
|
end
|
12
14
|
|
13
15
|
private
|
14
16
|
|
15
17
|
def compare(ref, can, path, diffs)
|
18
|
+
if (d = diffs.options[:max_depth])
|
19
|
+
return if path.depth > d + 1
|
20
|
+
end
|
21
|
+
|
16
22
|
case types(ref, can)
|
17
23
|
when [ :array, :array ] ; compare_arrays ref, can, path, diffs
|
18
24
|
when [ :hash, :hash ] ; compare_hashes ref, can, path, diffs
|
data/lib/check_please/diff.rb
CHANGED
@@ -11,14 +11,6 @@ module CheckPlease
|
|
11
11
|
@path = path.to_s
|
12
12
|
end
|
13
13
|
|
14
|
-
def ref_display
|
15
|
-
reference.inspect
|
16
|
-
end
|
17
|
-
|
18
|
-
def can_display
|
19
|
-
candidate.inspect
|
20
|
-
end
|
21
|
-
|
22
14
|
def attributes
|
23
15
|
Hash[ COLUMNS.map { |name| [ name, send(name) ] } ]
|
24
16
|
end
|
@@ -28,8 +20,8 @@ module CheckPlease
|
|
28
20
|
s << self.class.name
|
29
21
|
s << " type=#{type}"
|
30
22
|
s << " path=#{path}"
|
31
|
-
s << " ref=#{
|
32
|
-
s << " can=#{
|
23
|
+
s << " ref=#{reference.inspect}"
|
24
|
+
s << " can=#{candidate.inspect}"
|
33
25
|
s << ">"
|
34
26
|
s
|
35
27
|
end
|
data/lib/check_please/diffs.rb
CHANGED
@@ -5,7 +5,9 @@ module CheckPlease
|
|
5
5
|
# Custom collection class for Diff instances.
|
6
6
|
# Can retrieve members using indexes or paths.
|
7
7
|
class Diffs
|
8
|
-
|
8
|
+
attr_reader :options
|
9
|
+
def initialize(diff_list = nil, options: {})
|
10
|
+
@options = options
|
9
11
|
@list = []
|
10
12
|
@hash = {}
|
11
13
|
Array(diff_list).each do |diff|
|
@@ -27,6 +29,11 @@ module CheckPlease
|
|
27
29
|
end
|
28
30
|
|
29
31
|
def <<(diff)
|
32
|
+
if (n = options[:max_diffs])
|
33
|
+
# It seems no one can help me now / I'm in too deep, there's no way out
|
34
|
+
throw :max_diffs_reached if length >= n
|
35
|
+
end
|
36
|
+
|
30
37
|
@list << diff
|
31
38
|
@hash[diff.path] = diff
|
32
39
|
end
|
data/lib/check_please/path.rb
CHANGED
@@ -4,21 +4,30 @@ module CheckPlease
|
|
4
4
|
module Printers
|
5
5
|
|
6
6
|
class TablePrint < Base
|
7
|
+
InspectStrings = Object.new.tap do |obj|
|
8
|
+
def obj.format(value)
|
9
|
+
value.is_a?(String) ? value.inspect : value
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
PATH_MAX_WIDTH = 250 # if you hit this limit, you have other problems
|
14
|
+
|
7
15
|
TP_OPTS = [
|
8
|
-
:
|
9
|
-
{ :
|
10
|
-
:
|
11
|
-
:
|
16
|
+
{ type: { display_name: "Type" } },
|
17
|
+
{ path: { display_name: "Path", width: PATH_MAX_WIDTH } },
|
18
|
+
{ reference: { display_name: "Reference", formatters: [ InspectStrings ] } },
|
19
|
+
{ candidate: { display_name: "Candidate", formatters: [ InspectStrings ] } },
|
12
20
|
]
|
13
21
|
|
14
22
|
def to_s
|
15
23
|
return "" if diffs.empty?
|
16
24
|
|
17
|
-
build_string do |io|
|
25
|
+
out = build_string do |io|
|
18
26
|
switch_tableprint_io(io) do
|
19
27
|
tp diffs.data, *TP_OPTS
|
20
28
|
end
|
21
29
|
end
|
30
|
+
strip_trailing_whitespace(out)
|
22
31
|
end
|
23
32
|
|
24
33
|
private
|
@@ -31,6 +40,10 @@ module Printers
|
|
31
40
|
ensure
|
32
41
|
config.io = @old_io
|
33
42
|
end
|
43
|
+
|
44
|
+
def strip_trailing_whitespace(s)
|
45
|
+
s.lines.map(&:rstrip).join("\n")
|
46
|
+
end
|
34
47
|
end
|
35
48
|
|
36
49
|
end
|
data/lib/check_please/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: check_please
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sam Livingston-Gray
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-11-
|
11
|
+
date: 2020-11-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: table_print
|
@@ -56,7 +56,8 @@ description: Check for differences between two JSON strings (or Ruby data struct
|
|
56
56
|
parsed from them)
|
57
57
|
email:
|
58
58
|
- geeksam@gmail.com
|
59
|
-
executables:
|
59
|
+
executables:
|
60
|
+
- check_please
|
60
61
|
extensions: []
|
61
62
|
extra_rdoc_files: []
|
62
63
|
files:
|
@@ -69,10 +70,10 @@ files:
|
|
69
70
|
- LICENSE.txt
|
70
71
|
- README.md
|
71
72
|
- Rakefile
|
72
|
-
- bin/check_please
|
73
73
|
- bin/console
|
74
74
|
- bin/setup
|
75
75
|
- check_please.gemspec
|
76
|
+
- exe/check_please
|
76
77
|
- lib/check_please.rb
|
77
78
|
- lib/check_please/cli.rb
|
78
79
|
- lib/check_please/cli/flag.rb
|