check_please 0.4.0 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c4e0e81da97eb41aade7d58c516519bde3d2527173b64e7d644e46458493450d
4
- data.tar.gz: c633c13e0a776bdbf1691b9c01d3be3cc978cb445c2fbf8d74e95449ea0b3118
3
+ metadata.gz: d8c12644a3f83d1d263374a88b825a9f705f55d68582e90a50a3065ec2b0105e
4
+ data.tar.gz: 3f5ddc8b0d9e5c02bc4845e5a4ac21d07b358f75639146f32c71143bc1e6d605
5
5
  SHA512:
6
- metadata.gz: ad31ad99aef3494a2d2438b422a5fef9385c6fd05572131907d33d9e1e30a94ba0582ca4aae00c997dbe302487719da3d5ddbf715cf23422405086084f7a982c
7
- data.tar.gz: 04d8e89b72894e458394ff0428192ffbe6f3b307437ce6ee8496f6e3064236498286b2fb203a399c5245172cec391656646dac39b20f0adc8a345eff13b8f015
6
+ metadata.gz: c95071934105bc8c8876287bf460a0466ecc996eab6356c64a994566824c95d2f24d6d446bc8bcc6d4d66b0a224023a4347839e75f5d8204860e9e2210e7e07d
7
+ data.tar.gz: ebdbe6bdeafd3f71bf9461f5eccd73f20772043d9e84ab01fa91c0a1dcff054a21c74d718aa40cf0c89bed3219d36d1f46e89ce37ea6d27483bb268518bb7d59
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.*
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- check_please (0.4.0)
4
+ check_please (0.5.3)
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
+ * [Repeatable Flags](#repeatable-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,7 +48,7 @@ Or install it yourself as:
19
48
 
20
49
  $ gem install check_please
21
50
 
22
- ## Terminology
51
+ # Terminology
23
52
 
24
53
  I know, you just want to see how to use this thing. Feel free to scroll down,
25
54
  but be aware that CheckPlease uses a few words in a jargony way:
@@ -35,9 +64,14 @@ but be aware that CheckPlease uses a few words in a jargony way:
35
64
  **reference** and the **candidate**. More on this in "Understanding the Output",
36
65
  below.
37
66
 
38
- ## Usage
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. :)
39
71
 
40
- ### From the Terminal
72
+ # Usage
73
+
74
+ ## From the Terminal / Command Line Interface (CLI)
41
75
 
42
76
  Use the `bin/check_please` executable. (To get started, run it with the '-h' flag.)
43
77
 
@@ -48,7 +82,7 @@ of giving it a second filename as the argument. (This is especially useful if
48
82
  you're copying an XHR response out of a web browser's dev tools and have a tool
49
83
  like MacOS's `pbpaste` utility.)
50
84
 
51
- ### From RSpec
85
+ ## From RSpec
52
86
 
53
87
  See [check_please_rspec_matcher](https://github.com/RealGeeks/check_please_rspec_matcher).
54
88
 
@@ -57,14 +91,19 @@ like to provide custom logic for diffing your own classes, you might be better
57
91
  served by the [super_diff](https://github.com/mcmire/super_diff) gem. Check it
58
92
  out!
59
93
 
60
- ### From Ruby
94
+ ## From Ruby
95
+
96
+ See also: [./usage_examples.rb](usage_examples.rb).
61
97
 
62
98
  Create two strings, each containing a JSON or YAML document, and pass them to
63
99
  `CheckPlease.render_diff`. You'll get back a third string containing a report
64
- of all the differences CheckPlease found in the two JSON strings. (See also:
65
- [./usage_examples.rb](usage_examples.rb).)
100
+ of all the differences CheckPlease found in the two JSON strings.
101
+
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.
66
105
 
67
- ### Understanding the Output
106
+ ## Understanding the Output
68
107
 
69
108
  CheckPlease follows the Unix philosophy of "no news is good news". If your
70
109
  **candidate** matches your **reference**, you'll get an empty message.
@@ -118,7 +157,7 @@ mismatch | /meta/foo | spam | foo
118
157
 
119
158
  Let's start with the leftmost column...
120
159
 
121
- #### Diff Types
160
+ ### Diff Types
122
161
 
123
162
  The above example is intended to illustrate every possible type of diff that
124
163
  CheckPlease defines:
@@ -131,13 +170,14 @@ CheckPlease defines:
131
170
  and it stops when it encounters a type mismatch in order to avoid producing a
132
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.
173
+ value at the given path, and neither value was an Array or a Hash, and the
174
+ two values were not equal.
135
175
  * **extra** means that, inside an Array or a Hash, the **candidate** contained
136
- values that were not found in the **reference**.
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
182
  The second column contains a path expression. This is extremely lo-fi:
143
183
 
@@ -152,32 +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:
317
+
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
+ ```
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
+ }
163
396
 
164
- ## TODO
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
+ ```
165
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
432
  * detect timestamps and compare after parsing?
169
433
  * ignore sub-second precision (option / CLI flag)?
170
434
  * possibly support plugins for other folks to add custom coercions?
171
- * support expressions of specific paths to ignore
172
- * wildcards? `#` for indexes, `**` to match one or more path segments?
173
- (This could get ugly fast.)
174
435
  * display filters? (e.g., { a: 1, b: 2 } ==> "Hash#3")
175
436
  * shorter descriptions of values with different classes
176
437
  (but maybe just the existing :type_mismatch diffs?)
177
438
  * another "possibly support plugins" expansion point here
178
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...
179
450
 
180
- ## Development
451
+ -----
452
+
453
+ # Development
181
454
 
182
455
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
183
456
  `rake spec` to run the tests. You can also run `bin/console` for an interactive
@@ -189,22 +462,21 @@ release a new version, update the version number in `version.rb`, and then run
189
462
  git commits and tags, and push the `.gem` file to
190
463
  [rubygems.org](https://rubygems.org).
191
464
 
192
- ## Contributing
465
+ # Contributing
193
466
 
194
467
  Bug reports and pull requests are welcome on GitHub at
195
- 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
196
469
  safe, welcoming space for collaboration, and contributors are expected to
197
470
  adhere to the [code of
198
- conduct](https://github.com/[USERNAME]/check_please/blob/master/CODE_OF_CONDUCT.md).
199
-
471
+ conduct](https://github.com/RealGeeks/check_please/blob/master/CODE_OF_CONDUCT.md).
200
472
 
201
- ## License
473
+ # License
202
474
 
203
475
  The gem is available as open source under the terms of the [MIT
204
476
  License](https://opensource.org/licenses/MIT).
205
477
 
206
- ## Code of Conduct
478
+ # Code of Conduct
207
479
 
208
480
  Everyone interacting in the CheckPlease project's codebases, issue trackers,
209
481
  chat rooms and mailing lists is expected to follow the [code of
210
- 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).