@@ -35,11 +35,9 @@ but be aware that 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
- TODO: document --match-by-key, including "key expressions" and "key/value expressions"
39
-
40
38
## Usage
41
39
42
- ### From the Terminal
40
+ ### From the Terminal / Command Line Interface (CLI)
43
41
44
42
Use the ` bin/check_please ` executable. (To get started, run it with the '-h' flag.)
45
43
@@ -165,9 +163,208 @@ CheckPlease produces tabular output by default. (It leans heavily on the
165
163
amazing [ table_print] ( http://tableprintgem.com ) gem for this.)
166
164
167
165
If you want to incorporate CheckPlease into some other toolchain, it can also
168
- print diffs as JSON to facilitate parsing. In Ruby, pass ` format: :json ` to
166
+ print diffs as JSON to facilitate parsing. How you do this depends on whether
167
+ you're using CheckPlease from the command line or in Ruby, which is a good time
168
+ to talk about...
169
+
170
+
171
+ In Ruby, pass ` format: :json ` to
169
172
` CheckPlease.render_diff ` ; in the CLI, use the ` -f ` /` --format ` switch.
170
173
174
+ ### Flags
175
+
176
+ CheckPlease has several flags that control its behavior.
177
+
178
+ For quick help on which flags are available, as well as some terse help text,
179
+ you can run the ` check_please ` executable with no arguments (or the ` -h ` or
180
+ ` --help ` flags if that makes you feel better).
181
+
182
+ While of course we aspire to keep this README up to date, it's probably best to
183
+ believe things in the following priority order:
184
+
185
+ * observed behavior
186
+ * the code (start from ` ./lib/check_please.rb ` and search for ` Flags.define ` ,
187
+ then trace through as needed)
188
+ * the tests (` spec/check_please/flags_spec.rb ` describes how the flags work;
189
+ from there, you'll have to search on the flag's name to see how it shows up
190
+ in code)
191
+ * the output of ` check_please --help `
192
+ * this README :)
193
+
194
+ All flags have exactly one "Ruby name" and one or more "CLI names". When the
195
+ CLI runs, it parses the values in ` ARGV ` (using Ruby's native ` OptionParser ` )
196
+ and uses that information to build a ` CheckPlease::Flags ` instance. After that
197
+ point, a flag will be referred to within the CheckPlease code exclusively by
198
+ its "Ruby name".
199
+
200
+ For example, the flag that controls the format in which diffs are displayed has
201
+ a Ruby name of ` format ` , and CLI names of ` -f ` and ` --format ` .
202
+
203
+ #### Setting Flags in the CLI
204
+
205
+ This should behave more or less as an experienced Unix CLI user might expect.
206
+
207
+ As such, you can specify, e.g., that you want output in JSON format using
208
+ either ` --format json ` or ` -f json ` .
209
+
210
+ (I might expand this section some day. In the meantime, if you are not yet an
211
+ experienced Unix CLI user, feel free to ask for help! You can either open an
212
+ issue or look for emails in the ` .gemspec ` file...)
213
+
214
+ #### Setting Flags in Ruby
215
+
216
+ All external API entry points allow you to specify flags using their Ruby names
217
+ in the idiomatic "options Hash at the end of the argument list" that should be
218
+ familiar to most Rubyists. (Again, I assume that, if you're using this tool, I
219
+ don't need to explain this further, but feel free to ask for help if you need
220
+ it.)
221
+
222
+ (Internally, CheckPlease immediately converts that options hash into a
223
+ ` CheckPlease::Flags ` object, but that should be considered an implementation
224
+ detail unless you're interested in hacking on CheckPlease itself.)
225
+
226
+ For example, to get back a String containing the diffs between two data
227
+ structures in JSON format, you might do:
228
+
229
+ ```
230
+ reference = { "foo" => "wibble" }
231
+ candidate = { "bar" => "wibble" }
232
+ puts CheckPlease.render_diff(
233
+ reference,
234
+ candidate,
235
+ format: :json # <--- flags
236
+ )
237
+ ```
238
+
239
+ #### "Reentrant" Flags
240
+
241
+ Several flags are "reentrant". This means that the flag and its associated
242
+ value ** may** appear more than once in the CLI. I've tried to make both the
243
+ CLI and the Ruby API follow their respective environment's conventions.
244
+
245
+ For example, if you want to specify a path to ignore using the
246
+ ` --reject-paths ` flag, you'd invoke the CLI like this:
247
+
248
+ * ` [bundle exec] check_please reference.json candidate.json --select-paths /foo `
249
+
250
+ And if you want to specify more than one path, that would look like:
251
+
252
+ * ` [bundle exec] check_please reference.json candidate.json --select-paths /foo --select-paths /bar `
253
+
254
+ In Ruby, you can specify this in the options hash as a single key with an Array
255
+ value:
256
+
257
+ * ` CheckPlease.render_diff(reference, candidate, select_paths: [ "/foo", "/bar" ]) `
258
+
259
+ _ (NOTE TO MAINTAINERS: internally, the way ` CheckPlease::CLI::Parser ` uses
260
+ Ruby's ` OptionParser ` leads to some less than obvious behavior. Search
261
+ ` ./spec/check_please/flags_spec.rb ` for the word "surprising" for details.)_
262
+
263
+ #### Expanded Documentation for Specific Flags
264
+
265
+ ##### ` match_by_key `
266
+
267
+ Okay. This gets... a bit weird, and as of this writing it's still a bit experimental.
268
+
269
+ For all the details on exactly how this behaves, go look in
270
+ ` ./spec/check_please/comparison_spec.rb ` and check out the ` describe ` block
271
+ labeled ` "comparing arrays by keys" ` .
272
+
273
+ The short version, though, is that this allows you to match up two lists of
274
+ hashes using the value of a single key that is treated as the identifier for
275
+ that hash.
276
+
277
+ There's a lot going on in that sentence, so let's unpack it a bit.
278
+
279
+ Imagine that you're comparing two documents that actually contain the same
280
+ data, but in a different order. To use a very simple example, let's say that
281
+ both documents consist of a single array of two simple hashes, which we'll call
282
+ "A" and "B":
283
+
284
+ * "A" looks like this: ` { "id" => 1, "foo" => "bar" } `
285
+ * "B" looks like this: ` { "id" => 2, "foo" => "spam" } `
286
+
287
+ Normally, if your reference document is the list [ A, B] and your candidate
288
+ document is the list [ B, A] , CheckPlease will compare A and B by their position
289
+ in the array, and you'll get a diff report that looks like this:
290
+
291
+ ```
292
+ TYPE | PATH | REFERENCE | CANDIDATE
293
+ ---------|--------|-----------|----------
294
+ mismatch | /1/id | 1 | 2
295
+ mismatch | /1/foo | "bar" | "bat"
296
+ mismatch | /2/id | 2 | 1
297
+ mismatch | /2/foo | "bat" | "bar"
298
+ ```
299
+
300
+ To solve this problem, CheckPlease adds a ** key expression** to its (very
301
+ simple) path syntax that lets you specify a ** key** to use to match up elements
302
+ in both lists, rather than simply comparing elements by position.
303
+
304
+ Continuing with the above example, if we give ` match_by_key ` a value of
305
+ ` ["/:id"] ` , it will use the "id" value in both hashes (remember, A's ` id ` is
306
+ ` 1 ` and B's ` id ` is ` 2 ` ) to identify every element in both the reference array
307
+ and the candidate array, and correctly match A and B, giving you an empty list
308
+ of diffs.
309
+
310
+ Please note that the CLI and Ruby implementations of these are a bit different
311
+ (see the '"Reentrant" Flags' section).
312
+
313
+ Here are some examples of how that looks on the command line:
314
+
315
+ * ` --match-by-key /:id ` -- using the A and B documents above, this will expect
316
+ the top-level element to be an array that contains only hashes, and use the
317
+ "id" value in each hash to match up reference/candidate pairs.
318
+
319
+ * ` --match-by-key /books/:isbn ` -- this will expect the top-level element to be
320
+ a hash with a 'books' key that refers to an array, and will use the "isbn"
321
+ value in each hash to match up reference/candidate pairs.
322
+
323
+ * ` --match-by-key /authors/:id/books/:isbn ` -- see below.
324
+
325
+ For that last one, the structure of the reference document will look like this:
326
+
327
+ ``` ruby
328
+ {
329
+ " authors" => [
330
+ {
331
+ " id" => 1 ,
332
+ " name" => " Anne Onymous" ,
333
+ " books" => [
334
+ { " isbn" => " 12345" , " title" => " Who Am I, Really?" },
335
+ # ...
336
+ ]
337
+ },
338
+ {
339
+ " id" => 2 ,
340
+ " name" => " Pseud Onymous" ,
341
+ " books" => [
342
+ { " isbn" => " 67890" , " title" => " You'll Never Know" },
343
+ # ...
344
+ ]
345
+ },
346
+ # ...
347
+ ]
348
+ }
349
+ ```
350
+
351
+ At the top level, CheckPlease will match up hash elements by key. When it gets
352
+ to the "authors" key, it will look at the ` match_by_key ` expression, see that
353
+ it's supposed to use the "id" key to compare elements in an array, and do so.
354
+ Further down, when it encounters the "books" key in both authors 1 and 2, it
355
+ will use the "isbn" key to match up elements in the "books" array.
356
+
357
+ Finally, if there are any diffs to report, CheckPlease uses a ** key/value
358
+ expression** to report mismatches. For example, if the reference had Anne
359
+ Onymous' book title as "Who Am I, Really?" and the candidate listed it as "Who
360
+ The Heck Am I?", CheckPlease would show this using the following path
361
+ expression: ` /authors/id=1/books/isbn=12345 `
362
+
363
+ ** This syntax is intended to be readable by humans first.** If you need to
364
+ build tooling on it... well, I'm open to suggestions. :)
365
+
366
+
367
+
171
368
## TODO (maybe)
172
369
173
370
* document flags for rspec matcher
0 commit comments