check_please 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|