check_please 0.2.3 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f794a7a96790b642f2afc6f66bebb761a788c2b2a95025662e4ec8e654e612ef
4
- data.tar.gz: 98a088234aa843abf60668d8ee6722de318f60d19e29352f8332338d28268e7c
3
+ metadata.gz: fe333e55fbfdb89d0ea68b3f9ceefde98f3aa4b798792e9dd13745b76757a499
4
+ data.tar.gz: cf8cfc0205d3b8d7a3d10ec1c0c5487ac140dd7bc41c0c640eb2d72ec3d1e3f7
5
5
  SHA512:
6
- metadata.gz: 376fe8e75cf2c952d0cbaf42c32ca659c4f96488919eda781fc29cb50b259ddd3a91d60aa4bbae48022ccce96ab04148e60938079920a7657b7e288061b707dc
7
- data.tar.gz: dd4c24bd01c391fa70e626bd9753efd3601a4a9a92dfe92956ce5742c0d8771e53a32a3ef05cbd1e45ac75eff30342230ba2b3dd238274fbe00bea2291f7d148
6
+ metadata.gz: 918e8b6edec715a8096872c46fa12adc029244b886525c704e9b769fecae6595d38d0b658ebe64a53ebe3474d6ddba76f7d8222baefbef582da4c7dec27e686e
7
+ data.tar.gz: 4e0da6a791cc4e539ed75f893a3950336bcf5703e6dbb6435e02bac23f94e3207c06dca5cf13551a4a81980474e4e796a911ea333bfec42b5d97aa1e6ef0f3c8
data/.gitignore CHANGED
@@ -10,4 +10,6 @@
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
12
  /vendor/
13
- spec/examples.txt
13
+ spec/examples.txt
14
+ README.md.orig.*
15
+ README.md.toc.*
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- check_please (0.2.3)
4
+ check_please (0.5.0)
5
5
  table_print
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -3,7 +3,36 @@
3
3
  Check for differences between two JSON documents, YAML documents, or Ruby data
4
4
  structures parsed from either of those.
5
5
 
6
- ## Installation
6
+ <!-- start of auto-generated TOC; see https://github.com/ekalinin/github-markdown-toc -->
7
+ <!--ts-->
8
+ * [check_please](#check_please)
9
+ * [Installation](#installation)
10
+ * [Terminology](#terminology)
11
+ * [Usage](#usage)
12
+ * [From the Terminal / Command Line Interface (CLI)](#from-the-terminal--command-line-interface-cli)
13
+ * [From RSpec](#from-rspec)
14
+ * [From Ruby](#from-ruby)
15
+ * [Understanding the Output](#understanding-the-output)
16
+ * [Diff Types](#diff-types)
17
+ * [Paths](#paths)
18
+ * [Output Formats](#output-formats)
19
+ * [Flags](#flags)
20
+ * [Setting Flags in the CLI](#setting-flags-in-the-cli)
21
+ * [Setting Flags in Ruby](#setting-flags-in-ruby)
22
+ * ["Reentrant" Flags](#reentrant-flags)
23
+ * [Expanded Documentation for Specific Flags](#expanded-documentation-for-specific-flags)
24
+ * [Flag: match_by_key](#flag-match_by_key)
25
+ * [TODO (maybe)](#todo-maybe)
26
+ * [Development](#development)
27
+ * [Contributing](#contributing)
28
+ * [License](#license)
29
+ * [Code of Conduct](#code-of-conduct)
30
+
31
+
32
+ <!--te-->
33
+ <!-- end of auto-generated TOC -->
34
+
35
+ # Installation
7
36
 
8
37
  Add this line to your application's Gemfile:
9
38
 
@@ -19,11 +48,10 @@ Or install it yourself as:
19
48
 
20
49
  $ gem install check_please
21
50
 
22
- ## Usage
51
+ # Terminology
23
52
 
24
- ### Terminology
25
-
26
- CheckPlease uses a few words in a jargony way:
53
+ I know, you just want to see how to use this thing. Feel free to scroll down,
54
+ but be aware that CheckPlease uses a few words in a jargony way:
27
55
 
28
56
  * **Reference** is always used to refer to the "target" or "source of truth."
29
57
  We assume you're comparing two things because you want one of them to be like
@@ -36,7 +64,14 @@ CheckPlease uses a few words in a jargony way:
36
64
  **reference** and the **candidate**. More on this in "Understanding the Output",
37
65
  below.
38
66
 
39
- ### CLI
67
+ Also, even though this gem was born from a need to compare JSON documents, I'll
68
+ be talking about "hashes" instead of "objects", because I assume this will
69
+ mostly be used by Ruby developers. Feel free to substitute "object" wherever
70
+ you see "hash" if that's easier for you. :)
71
+
72
+ # Usage
73
+
74
+ ## From the Terminal / Command Line Interface (CLI)
40
75
 
41
76
  Use the `bin/check_please` executable. (To get started, run it with the '-h' flag.)
42
77
 
@@ -47,21 +82,28 @@ of giving it a second filename as the argument. (This is especially useful if
47
82
  you're copying an XHR response out of a web browser's dev tools and have a tool
48
83
  like MacOS's `pbpaste` utility.)
49
84
 
50
- ### RSpec Matcher
85
+ ## From RSpec
51
86
 
52
87
  See [check_please_rspec_matcher](https://github.com/RealGeeks/check_please_rspec_matcher).
53
88
 
54
- ### From Within Ruby
89
+ If you'd like more control over the output formatting, and especially if you'd
90
+ like to provide custom logic for diffing your own classes, you might be better
91
+ served by the [super_diff](https://github.com/mcmire/super_diff) gem. Check it
92
+ out!
93
+
94
+ ## From Ruby
95
+
96
+ See also: [./usage_examples.rb](usage_examples.rb).
55
97
 
56
- Create two JSON strings and pass them to `CheckPlease.render_diff`. You'll get
57
- back a third string containing a nicely formatted report of all the differences
58
- CheckPlease found in the two JSON strings. (See also: [./usage_examples.rb](usage_examples.rb).)
98
+ Create two strings, each containing a JSON or YAML document, and pass them to
99
+ `CheckPlease.render_diff`. You'll get back a third string containing a report
100
+ of all the differences CheckPlease found in the two JSON strings.
59
101
 
60
- (You can also parse the JSON strings yourself and pass the resulting data
61
- structures in, if you're into that. I mean, I wrote this to help compare JSON
62
- data that's too big and complicated to scan through visually, but you do you!
102
+ Or, if you'd like to inspect the diffs in your own way, use `CheckPlease.diff`
103
+ instead. You'll get back a `CheckPlease::Diffs` custom collection that
104
+ contains `CheckPlease::Diff` instances.
63
105
 
64
- ### Understanding the Output
106
+ ## Understanding the Output
65
107
 
66
108
  CheckPlease follows the Unix philosophy of "no news is good news". If your
67
109
  **candidate** matches your **reference**, you'll get an empty message.
@@ -71,9 +113,7 @@ tool because you want a human-friendly summary of all the places that your
71
113
  **candidate** fell short.
72
114
 
73
115
  When CheckPlease compares your two samples, it generates a list of diffs to
74
- describe any discrepancies it encounters. (By default, it prints that list in a
75
- tabular format, but if you want to incorporate this into another toolchain,
76
- CheckPlease can also print these diffs as JSON to facilitate parsing.)
116
+ describe any discrepancies it encounters.
77
117
 
78
118
  An example would probably help here.
79
119
 
@@ -117,7 +157,7 @@ mismatch | /meta/foo | spam | foo
117
157
 
118
158
  Let's start with the leftmost column...
119
159
 
120
- #### Diff Types
160
+ ### Diff Types
121
161
 
122
162
  The above example is intended to illustrate every possible type of diff that
123
163
  CheckPlease defines:
@@ -125,23 +165,23 @@ CheckPlease defines:
125
165
  * **type_mismatch** means that both the **reference** and the **candidate** had
126
166
  a value at the given path, but one value was an Array or a Hash and the other
127
167
  was not. **When CheckPlease encounters a type mismatch, it does not compare
128
- anything "below" the given path.** producing a lot of "garbage" diffs.
129
- _(Technical note: CheckPlease uses a "recursive descent" strategy to
130
- traverse the **reference** data structure, and it stops when it encounters a
131
- type mismatch in order to avoid producing a lot of "garbage" diff output.
132
- Also, the way these get displayed is likely to change.)_
168
+ anything "below" the given path.** _(Technical note: CheckPlease uses a
169
+ "recursive descent" strategy to traverse the **reference** data structure,
170
+ and it stops when it encounters a type mismatch in order to avoid producing a
171
+ lot of "garbage" diff output.)_
133
172
  * **mismatch** means that both the **reference** and the **candidate** had a
134
- value at the given path, and neither value was an Array or a Hash.
135
- * **extra** means that, inside an Array or a Hash, the **candidate**
136
- contained values that were not found in the **reference**.
173
+ value at the given path, and neither value was an Array or a Hash, and the
174
+ two values were not equal.
175
+ * **extra** means that, inside an Array or a Hash, the **candidate** contained
176
+ elements that were not found in the **reference**.
137
177
  * **missing** is the opposite of **extra**: inside an Array or a Hash, the
138
- **reference** contained values that were not found in the **candidate**.
178
+ **reference** contained elements that were not found in the **candidate**.
139
179
 
140
- #### Paths
180
+ ### Paths
141
181
 
142
- The second column contains a path expression. This is extremely basic:
182
+ The second column contains a path expression. This is extremely lo-fi:
143
183
 
144
- * The first element in the data structure is defined as "/".
184
+ * The root of the data structure is defined as "/".
145
185
  * If an element in the data structure is an array, its child elements will have
146
186
  a **one-based** index appended to their parent's path.
147
187
  * If an element in the data structure is an object ("Hash" in Ruby), the key
@@ -152,33 +192,265 @@ _**Being primarily a Ruby developer, I'm quite ignorant of conventions in the
152
192
  JS community; if there's an existing convention for paths, please open an
153
193
  issue!**_
154
194
 
155
- #### Output Formats
195
+ ### Output Formats
156
196
 
157
197
  CheckPlease produces tabular output by default. (It leans heavily on the
158
198
  amazing [table_print](http://tableprintgem.com) gem for this.)
159
199
 
160
200
  If you want to incorporate CheckPlease into some other toolchain, it can also
161
- print diffs as JSON to facilitate parsing. In Ruby, pass `format: :json` to
162
- `CheckPlease.render_diff`; in the CLI, use the `-f`/`--format` switch.
201
+ print diffs as JSON to facilitate parsing. How you do this depends on whether
202
+ you're using CheckPlease from the command line or in Ruby, which is a good time
203
+ to talk about...
204
+
205
+ ## Flags
206
+
207
+ CheckPlease has several flags that control its behavior.
208
+
209
+ For quick help on which flags are available, as well as some terse help text,
210
+ you can run the `check_please` executable with no arguments (or the `-h` or
211
+ `--help` flags if that makes you feel better).
212
+
213
+ While of course we aspire to keep this README up to date, it's probably best to
214
+ believe things in the following priority order:
215
+
216
+ * observed behavior
217
+ * the code (start from `./lib/check_please.rb` and search for `Flags.define`,
218
+ then trace through as needed)
219
+ * the tests (`spec/check_please/flags_spec.rb` describes how the flags work;
220
+ from there, you'll have to search on the flag's name to see how it shows up
221
+ in code)
222
+ * the output of `check_please --help`
223
+ * this README :)
224
+
225
+ All flags have exactly one "Ruby name" and one or more "CLI names". When the
226
+ CLI runs, it parses the values in `ARGV` (using Ruby's native `OptionParser`)
227
+ and uses that information to build a `CheckPlease::Flags` instance. After that
228
+ point, a flag will be referred to within the CheckPlease code exclusively by
229
+ its "Ruby name".
230
+
231
+ For example, the flag that controls the format in which diffs are displayed has
232
+ a Ruby name of `format`, and CLI names of `-f` and `--format`.
233
+
234
+ ### Setting Flags in the CLI
235
+
236
+ This should behave more or less as an experienced Unix CLI user might expect.
237
+
238
+ As such, you can specify, e.g., that you want output in JSON format using
239
+ either `--format json` or `-f json`.
240
+
241
+ (I might expand this section some day. In the meantime, if you are not yet an
242
+ experienced Unix CLI user, feel free to ask for help! You can either open an
243
+ issue or look for emails in the `.gemspec` file...)
244
+
245
+ ### Setting Flags in Ruby
246
+
247
+ All external API entry points allow you to specify flags using their Ruby names
248
+ in the idiomatic "options Hash at the end of the argument list" that should be
249
+ familiar to most Rubyists. (Again, I assume that, if you're using this tool, I
250
+ don't need to explain this further, but feel free to ask for help if you need
251
+ it.)
252
+
253
+ (Internally, CheckPlease immediately converts that options hash into a
254
+ `CheckPlease::Flags` object, but that should be considered an implementation
255
+ detail unless you're interested in hacking on CheckPlease itself.)
256
+
257
+ For example, to get back a String containing the diffs between two data
258
+ structures in JSON format, you might do:
259
+
260
+ ```
261
+ reference = { "foo" => "wibble" }
262
+ candidate = { "bar" => "wibble" }
263
+ puts CheckPlease.render_diff(
264
+ reference,
265
+ candidate,
266
+ format: :json # <--- flags
267
+ )
268
+ ```
269
+
270
+ ### Repeatable Flags
271
+
272
+ Several flags **may** be specified more than once when invoking the CLI. I've
273
+ tried to make both the CLI and the Ruby API follow their respective
274
+ environment's conventions.
275
+
276
+ For example, if you want to specify a path to ignore using the
277
+ `--reject-paths` flag, you'd invoke the CLI like this:
278
+
279
+ * `[bundle exec] check_please reference.json candidate.json --select-paths /foo`
280
+
281
+ And if you want to specify more than one path, that would look like:
282
+
283
+ * `[bundle exec] check_please reference.json candidate.json --select-paths /foo --select-paths /bar`
284
+
285
+ In Ruby, you can specify this in the options hash as a single key with an Array
286
+ value:
287
+
288
+ * `CheckPlease.render_diff(reference, candidate, select_paths: [ "/foo", "/bar" ])`
289
+
290
+ _(NOTE TO MAINTAINERS: internally, the way `CheckPlease::CLI::Parser` uses
291
+ Ruby's `OptionParser` leads to some less than obvious behavior. Search
292
+ [./spec/check_please/flags_spec.rb](spec/check_please/flags_spec.rb) for the
293
+ word "surprising" for details.)_
294
+
295
+ ### Expanded Documentation for Specific Flags
296
+
297
+ #### Flag: `match_by_key`
298
+
299
+ > **I know this looks like a LOT of information, but it's really not that
300
+ > bad!** This feature just requires specific examples to describe, and talking
301
+ > about it in English (rather than code) is hard. Take a moment for some deep
302
+ > breaths if you need it. :)
303
+
304
+ > _If you're comfortable reading RSpec and/or want to check out all the edge
305
+ > cases, go look in `./spec/check_please/comparison_spec.rb` and check out the
306
+ > `describe` block labeled `"comparing arrays by keys"`._
307
+
308
+ The `match_by_key` flag allows you to match up arrays of hashes using the value
309
+ of a single key that is treated as the identifier for each hash.
310
+
311
+ There's a lot going on in that sentence, so let's unpack it a bit.
312
+
313
+ Imagine you're comparing two documents that contain the same data, but in
314
+ different orders. To use a contrived example, let's say that both documents
315
+ consist of a single array of two simple hashes, but the reference array and the
316
+ candidate array are reversed:
163
317
 
164
- ## TODO
318
+ ```ruby
319
+ # REFERENCE
320
+ [ { "id" => 1, "foo" => "bar" }, { "id" => 2, "foo" => "spam" } ]
321
+
322
+ # CANDIDATE
323
+ [ { "id" => 2, "foo" => "spam" }, { "id" => 1, "foo" => "bar" } ]
324
+ ```
325
+
326
+ By default, CheckPlease will match up array elements by their position in the
327
+ array, resulting in a diff report like this:
328
+
329
+ ```
330
+ TYPE | PATH | REFERENCE | CANDIDATE
331
+ ---------|--------|-----------|----------
332
+ mismatch | /1/id | 1 | 2
333
+ mismatch | /1/foo | "bar" | "bat"
334
+ mismatch | /2/id | 2 | 1
335
+ mismatch | /2/foo | "bat" | "bar"
336
+ ```
337
+
338
+ To solve this problem, CheckPlease adds a **key expression** to its (very
339
+ simple) path syntax that lets you specify a **key** to use to match up elements
340
+ in both lists, rather than simply comparing elements by position.
341
+
342
+ Continuing with the above example, if we give `match_by_key` a value of
343
+ `["/:id"]`, it will use the "id" value in both hashes (remember, A's `id` is
344
+ `1` and B's `id` is `2`) to identify every element in both the reference array
345
+ and the candidate array, and correctly match A and B, giving you an empty list
346
+ of diffs.
347
+
348
+ Please note that the CLI and Ruby implementations of these are a bit different
349
+ (see "Setting Flags in the CLI" versus "Setting Flags in Ruby"), so if you're
350
+ doing this from the command line, it'll look like: `--match-by-key /:id`
351
+
352
+ Here, have another example. If you want to specify a match_by_key expression
353
+ below the root of the document, you can put the **key expression** further down
354
+ the path: `/books/:isbn`
355
+
356
+ This would correctly match up the following documents:
357
+
358
+ ```ruby
359
+ # REFERENCE
360
+ {
361
+ "books" => [
362
+ { "isbn" => "12345", "title" => "Who Am I, Really?" },
363
+ { "isbn" => "67890", "title" => "Who Are Any Of Us, Really?" },
364
+ ]
365
+ }
366
+
367
+ # CANDIDATE
368
+ {
369
+ "books" => [
370
+ { "isbn" => "67890", "title" => "Who Are Any Of Us, Really?" },
371
+ { "isbn" => "12345", "title" => "Who Am I, Really?" },
372
+ ]
373
+ }
374
+ ```
165
375
 
376
+ Finally, if you have deeply nested data with arrays of hashes at multiple
377
+ levels, you can specify more than one **key expression** in a single path,
378
+ like: `/authors/:id/books/:isbn`
379
+
380
+ This would correctly match up the following documents:
381
+
382
+ ```ruby
383
+ # REFERENCE
384
+ {
385
+ "authors" => [
386
+ {
387
+ "id" => 1,
388
+ "name" => "Anne Onymous",
389
+ "books" => [
390
+ { "isbn" => "12345", "title" => "Who Am I, Really?" },
391
+ { "isbn" => "67890", "title" => "Who Are Any Of Us, Really?" },
392
+ ]
393
+ },
394
+ ]
395
+ }
396
+
397
+ # CANDIDATE
398
+ {
399
+ "authors" => [
400
+ {
401
+ "id" => 1,
402
+ "name" => "Anne Onymous",
403
+ "books" => [
404
+ { "isbn" => "67890", "title" => "Who Are Any Of Us, Really?" },
405
+ { "isbn" => "12345", "title" => "Who Am I, Really?" },
406
+ ]
407
+ },
408
+ ]
409
+ }
410
+ ```
411
+
412
+ Finally, if there are any diffs to report, CheckPlease uses a **key/value
413
+ expression** to report mismatches.
414
+
415
+ Using the last example above (the one with `/authors/:id/books/:isbn`), if the
416
+ reference had Anne Onymous' book title as "Who Am I, Really?" and the candidate
417
+ listed it as "Who The Heck Am I?", CheckPlease would show the mismatch using
418
+ the following path expression: `/authors/id=1/books/isbn=12345`
419
+
420
+ **This syntax is intended to be readable by humans first.** If you need to
421
+ build tooling that consumes it... well, I'm open to suggestions. :)
422
+
423
+ -----
424
+
425
+ # TODO (maybe)
426
+
427
+ * document flags for rspec matcher
166
428
  * command line flags for :allthethings:!
429
+ * change display width for table format
430
+ (for example, "2020-07-16T19:42:41.312978" gets cut off)
167
431
  * sort by path?
168
- * max depth (for iterative refinement?)
169
432
  * detect timestamps and compare after parsing?
170
433
  * ignore sub-second precision (option / CLI flag)?
171
434
  * possibly support plugins for other folks to add custom coercions?
172
- * support expressions of specific paths to ignore
173
- * wildcards? `#` for indexes, `**` to match one or more path segments?
174
- (This could get ugly fast.)
175
435
  * display filters? (e.g., { a: 1, b: 2 } ==> "Hash#3")
176
436
  * shorter descriptions of values with different classes
177
437
  (but maybe just the existing :type_mismatch diffs?)
178
438
  * another "possibly support plugins" expansion point here
179
439
  * more output formats, maybe?
440
+ * [0xABAD1DEA] support wildcards in --select-paths and --reject-paths?
441
+ * `#` for indexes, `**` to match one or more path segments?
442
+ (This could get ugly fast.)
443
+ * [0xABAD1DEA] look for a config file in ./.check_please_config or ~/.check_please_config,
444
+ combine flags found there with those in ARGV in order of precedence:
445
+ 1) ARGV
446
+ 2) ./.check_please
447
+ 3) ~/.check_please
448
+ * but this may not actually be worth the time and complexity to implement, so
449
+ think about this first...
180
450
 
181
- ## Development
451
+ -----
452
+
453
+ # Development
182
454
 
183
455
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
184
456
  `rake spec` to run the tests. You can also run `bin/console` for an interactive
@@ -190,22 +462,21 @@ release a new version, update the version number in `version.rb`, and then run
190
462
  git commits and tags, and push the `.gem` file to
191
463
  [rubygems.org](https://rubygems.org).
192
464
 
193
- ## Contributing
465
+ # Contributing
194
466
 
195
467
  Bug reports and pull requests are welcome on GitHub at
196
- https://github.com/[USERNAME]/check_please. This project is intended to be a
468
+ https://github.com/RealGeeks/check_please. This project is intended to be a
197
469
  safe, welcoming space for collaboration, and contributors are expected to
198
470
  adhere to the [code of
199
- conduct](https://github.com/[USERNAME]/check_please/blob/master/CODE_OF_CONDUCT.md).
200
-
471
+ conduct](https://github.com/RealGeeks/check_please/blob/master/CODE_OF_CONDUCT.md).
201
472
 
202
- ## License
473
+ # License
203
474
 
204
475
  The gem is available as open source under the terms of the [MIT
205
476
  License](https://opensource.org/licenses/MIT).
206
477
 
207
- ## Code of Conduct
478
+ # Code of Conduct
208
479
 
209
480
  Everyone interacting in the CheckPlease project's codebases, issue trackers,
210
481
  chat rooms and mailing lists is expected to follow the [code of
211
- conduct](https://github.com/[USERNAME]/check_please/blob/master/CODE_OF_CONDUCT.md).
482
+ conduct](https://github.com/RealGeeks/check_please/blob/master/CODE_OF_CONDUCT.md).