check_please 0.4.1 → 0.5.0

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: 41ad78ce4324c1b5d9c8ac3e336d46108850d6fd7d1d51d30c13b807632d612a
4
- data.tar.gz: 9191e6e52c57b6d79761a8a0258a41378cf19af1364a3e8875e727ff8e501f06
3
+ metadata.gz: fe333e55fbfdb89d0ea68b3f9ceefde98f3aa4b798792e9dd13745b76757a499
4
+ data.tar.gz: cf8cfc0205d3b8d7a3d10ec1c0c5487ac140dd7bc41c0c640eb2d72ec3d1e3f7
5
5
  SHA512:
6
- metadata.gz: 388b93c0277580a37b066b7fa6aade4f8feeb5f677a4ccc2221ac060d23df153c3dc16c6cdacce740448773665200855e653d74f55cfae659511e39a4391b36f
7
- data.tar.gz: 44cb45ace2a720d90035eef279f490896a785e76c77bd4bd72c63400a828300332638442599b905dad8e4bde4b329a698fd44f4116f847699b5250c586e27c86
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.4.1)
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,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,7 +91,7 @@ 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
61
95
 
62
96
  See also: [./usage_examples.rb](usage_examples.rb).
63
97
 
@@ -69,7 +103,7 @@ Or, if you'd like to inspect the diffs in your own way, use `CheckPlease.diff`
69
103
  instead. You'll get back a `CheckPlease::Diffs` custom collection that
70
104
  contains `CheckPlease::Diff` instances.
71
105
 
72
- ### Understanding the Output
106
+ ## Understanding the Output
73
107
 
74
108
  CheckPlease follows the Unix philosophy of "no news is good news". If your
75
109
  **candidate** matches your **reference**, you'll get an empty message.
@@ -123,7 +157,7 @@ mismatch | /meta/foo | spam | foo
123
157
 
124
158
  Let's start with the leftmost column...
125
159
 
126
- #### Diff Types
160
+ ### Diff Types
127
161
 
128
162
  The above example is intended to illustrate every possible type of diff that
129
163
  CheckPlease defines:
@@ -136,13 +170,14 @@ CheckPlease defines:
136
170
  and it stops when it encounters a type mismatch in order to avoid producing a
137
171
  lot of "garbage" diff output.)_
138
172
  * **mismatch** means that both the **reference** and the **candidate** had a
139
- 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.
140
175
  * **extra** means that, inside an Array or a Hash, the **candidate** contained
141
- values that were not found in the **reference**.
176
+ elements that were not found in the **reference**.
142
177
  * **missing** is the opposite of **extra**: inside an Array or a Hash, the
143
- **reference** contained values that were not found in the **candidate**.
178
+ **reference** contained elements that were not found in the **candidate**.
144
179
 
145
- #### Paths
180
+ ### Paths
146
181
 
147
182
  The second column contains a path expression. This is extremely lo-fi:
148
183
 
@@ -157,18 +192,242 @@ _**Being primarily a Ruby developer, I'm quite ignorant of conventions in the
157
192
  JS community; if there's an existing convention for paths, please open an
158
193
  issue!**_
159
194
 
160
- #### Output Formats
195
+ ### Output Formats
161
196
 
162
197
  CheckPlease produces tabular output by default. (It leans heavily on the
163
198
  amazing [table_print](http://tableprintgem.com) gem for this.)
164
199
 
165
200
  If you want to incorporate CheckPlease into some other toolchain, it can also
166
- print diffs as JSON to facilitate parsing. In Ruby, pass `format: :json` to
167
- `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
+ }
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`
168
419
 
169
- ## TODO (maybe)
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. :)
170
422
 
423
+ -----
424
+
425
+ # TODO (maybe)
426
+
427
+ * document flags for rspec matcher
171
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)
172
431
  * sort by path?
173
432
  * detect timestamps and compare after parsing?
174
433
  * ignore sub-second precision (option / CLI flag)?
@@ -189,7 +448,9 @@ print diffs as JSON to facilitate parsing. In Ruby, pass `format: :json` to
189
448
  * but this may not actually be worth the time and complexity to implement, so
190
449
  think about this first...
191
450
 
192
- ## Development
451
+ -----
452
+
453
+ # Development
193
454
 
194
455
  After checking out the repo, run `bin/setup` to install dependencies. Then, run
195
456
  `rake spec` to run the tests. You can also run `bin/console` for an interactive
@@ -201,22 +462,21 @@ release a new version, update the version number in `version.rb`, and then run
201
462
  git commits and tags, and push the `.gem` file to
202
463
  [rubygems.org](https://rubygems.org).
203
464
 
204
- ## Contributing
465
+ # Contributing
205
466
 
206
467
  Bug reports and pull requests are welcome on GitHub at
207
- 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
208
469
  safe, welcoming space for collaboration, and contributors are expected to
209
470
  adhere to the [code of
210
- conduct](https://github.com/[USERNAME]/check_please/blob/master/CODE_OF_CONDUCT.md).
211
-
471
+ conduct](https://github.com/RealGeeks/check_please/blob/master/CODE_OF_CONDUCT.md).
212
472
 
213
- ## License
473
+ # License
214
474
 
215
475
  The gem is available as open source under the terms of the [MIT
216
476
  License](https://opensource.org/licenses/MIT).
217
477
 
218
- ## Code of Conduct
478
+ # Code of Conduct
219
479
 
220
480
  Everyone interacting in the CheckPlease project's codebases, issue trackers,
221
481
  chat rooms and mailing lists is expected to follow the [code of
222
- 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).
data/Rakefile CHANGED
@@ -1,21 +1,64 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "pathname"
4
+ require "tempfile"
3
5
 
4
- namespace :spec do
5
- RSpec::Core::RakeTask.new(:all)
6
6
 
7
+ PROJECT_ROOT = Pathname.new(File.dirname(__FILE__))
8
+
9
+
10
+ namespace :spec do
11
+ desc "All tests *except* those that exercise the executable CLI"
7
12
  RSpec::Core::RakeTask.new(:not_cli) do |t|
8
13
  t.rspec_opts = "--tag ~cli"
9
14
  end
15
+
16
+ desc "fast tests only"
10
17
  task fast: :not_cli
11
18
 
12
19
  # These are much slower than the rest, since they use Kernel#`
20
+ desc "Only tests that exercise the executable CLI (slower)"
13
21
  RSpec::Core::RakeTask.new(:cli) do |t|
14
22
  t.rspec_opts = "--tag cli"
15
23
  end
24
+
25
+ desc "approve changes to the CLI's `--help` output"
26
+ task :approve_cli_help_output do
27
+ output = `exe/check_please`
28
+ File.open(PROJECT_ROOT.join("spec/fixtures/cli-help-output"), "w") do |f|
29
+ f << output
30
+ end
31
+ end
16
32
  end
17
33
 
18
34
  # By default, `rake spec` should run fast specs first, then cli if those all pass
19
- task :spec => [ "spec:not_cli", "spec:cli" ]
35
+ desc "Run all tests (fast tests first, then the slower CLI ones)"
36
+ task :spec => [ "spec:fast", "spec:cli" ]
20
37
 
21
38
  task :default => :spec
39
+
40
+
41
+
42
+
43
+ desc "Generate TOC for the README"
44
+ task :toc do
45
+ # the `--no-backup` flag skips the creation of README.md.* backup files,
46
+ # WHICH IS FINE because we're using Git
47
+ puts "generating TOC..."
48
+ `bin/gh-md-toc --no-backup README.md`
49
+
50
+ # Now, strip out the 'Added by:` line so we can detect if there were actual changes
51
+ # Use a tempfile just in case sed barfs, I guess?
52
+ tmp = Tempfile.new('check-please-readme')
53
+ begin
54
+ `sed '/Added by: /d' README.md > #{tmp.path}`
55
+ FileUtils.mv tmp.path, PROJECT_ROOT.join("README.md")
56
+ ensure
57
+ tmp.close
58
+ tmp.unlink
59
+ end
60
+ end
61
+
62
+ # By making TOC generation a prerequisite of release, we *should* at least be
63
+ # forced to update the TOC whenever we publish a new version of the gem...
64
+ task :release => :toc