check_please 0.1.0 → 0.2.4

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: e9e2ff8cbe19131bcf4995eb057ef566bc8f4f102e138d7db5dc908befa1fbcf
4
- data.tar.gz: 616388b3ee563b7f384b7fc19b051117c0f83bd00e47092f7f6f7b9ec712c81b
3
+ metadata.gz: c4433e70c9c7da92bbc95c1bfb2f22a947be4e3d30c9fa5b1d735826cc45dde1
4
+ data.tar.gz: 249804476d1b19c1f9add240b20f5e05b9258d4a70f992b5bc8a3654339e7f5c
5
5
  SHA512:
6
- metadata.gz: f4b7bcef1fda2618631f59fe8ad17a2630ba33271a2c6e295b96f204076c29ba283b3bb19cfba1c8550cc03ecd709fd1aa9ecbf22db4846d3d1f7b302b7c4ee5
7
- data.tar.gz: 18be8f0813b99bb66930ceb5dc2b658b377522c84d77ed3361daaecfc1aaff08f439582bb0e63056e8f8617239379c25ed44b8a517d7d8eaf92834b616d77267
6
+ metadata.gz: be7a4320ecb7bf5836ed62ed629e3445b20e2e847e497847a5cdbab856694b46b7f039e8c423622b0b35f5d098398c1f060655e9f4f11aacc2b15adeeb1dda61
7
+ data.tar.gz: e15695cfa60a8c2cf29b54b32c33ce76a5758fe76cd8ac92cb90b362d1597060de469b99c4a2e182a5b35b28b4d1757508b6e8b058a6371fd7455ad2a30d7ae3
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- check_please (0.1.0)
4
+ check_please (0.2.4)
5
5
  table_print
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
- # CheckPlease
1
+ # check_please
2
2
 
3
- Check for differences between two JSON strings (or Ruby data structures parsed from them).
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,29 +19,147 @@ Or install it yourself as:
18
19
 
19
20
  $ gem install check_please
20
21
 
22
+ ## Terminology
23
+
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
+
27
+ * **Reference** is always used to refer to the "target" or "source of truth."
28
+ We assume you're comparing two things because you want one of them to be like
29
+ the other; the **reference** is what you're aiming for.
30
+ * **Candidate** is always used to refer to some JSON you'd like to compare
31
+ against the **reference**. _(We could've also used "sample," but it turns
32
+ out that "reference" and "candidate" are the same length, which makes code
33
+ line up neatly in a monospaced font...)_
34
+ * A **diff** is what CheckPlease calls an individual discrepancy between the
35
+ **reference** and the **candidate**. More on this in "Understanding the Output",
36
+ below.
37
+
21
38
  ## Usage
22
39
 
23
- ### CLI
40
+ ### From the Terminal
24
41
 
25
42
  Use the `bin/check_please` executable. (To get started, run it with the '-h' flag.)
26
43
 
27
- ### From Within Ruby
44
+ Note that the executable assumes you've saved your **reference** to a file.
45
+ Once that's done, you can either save the **candidate** to a file as well if
46
+ that fits your workflow, **or** you can pipe it to `bin/check_please` in lieu
47
+ of giving it a second filename as the argument. (This is especially useful if
48
+ you're copying an XHR response out of a web browser's dev tools and have a tool
49
+ like MacOS's `pbpaste` utility.)
50
+
51
+ ### From RSpec
52
+
53
+ See [check_please_rspec_matcher](https://github.com/RealGeeks/check_please_rspec_matcher).
54
+
55
+ ### From Ruby
56
+
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).)
61
+
62
+ ### Understanding the Output
63
+
64
+ CheckPlease follows the Unix philosophy of "no news is good news". If your
65
+ **candidate** matches your **reference**, you'll get an empty message.
66
+
67
+ But let's be honest: how often is that going to happen? No, you're using this
68
+ tool because you want a human-friendly summary of all the places that your
69
+ **candidate** fell short.
70
+
71
+ When CheckPlease compares your two samples, it generates a list of diffs to
72
+ describe any discrepancies it encounters.
73
+
74
+ An example would probably help here.
75
+
76
+ _(NOTE: these examples may fall out of date with the code. They're swiped
77
+ from [the CLI integration spec](spec/cli_integration_spec.rb), so please
78
+ consider that more authoritative than this README. If you do spot a
79
+ difference, please feel free to open an issue!)_
80
+
81
+ Given the following **reference** JSON:
82
+ ```
83
+ {
84
+ "id": 42,
85
+ "name": "The Answer",
86
+ "words": [ "what", "do", "you", "get", "when", "you", "multiply", "six", "by", "nine" ],
87
+ "meta": { "foo": "spam", "bar": "eggs", "yak": "bacon" }
88
+ }
89
+ ```
90
+
91
+ And the following **candidate** JSON:
92
+ ```
93
+ {
94
+ "id": 42,
95
+ "name": [ "I am large, and contain multitudes." ],
96
+ "words": [ "what", "do", "we", "get", "when", "I", "multiply", "six", "by", "nine", "dude" ],
97
+ "meta": { "foo": "foo", "yak": "bacon" }
98
+ }
99
+ ```
100
+
101
+ CheckPlease should produce the following output:
102
+
103
+ ```
104
+ TYPE | PATH | REFERENCE | CANDIDATE
105
+ --------------|-----------|------------|-------------------------------
106
+ type_mismatch | /name | The Answer | ["I am large, and contain m...
107
+ mismatch | /words/3 | you | we
108
+ mismatch | /words/6 | you | I
109
+ extra | /words/11 | | dude
110
+ missing | /meta/bar | eggs |
111
+ mismatch | /meta/foo | spam | foo
112
+ ```
113
+
114
+ Let's start with the leftmost column...
115
+
116
+ #### Diff Types
117
+
118
+ The above example is intended to illustrate every possible type of diff that
119
+ CheckPlease defines:
120
+
121
+ * **type_mismatch** means that both the **reference** and the **candidate** had
122
+ a value at the given path, but one value was an Array or a Hash and the other
123
+ was not. **When CheckPlease encounters a type mismatch, it does not compare
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.)_
128
+ * **mismatch** means that both the **reference** and the **candidate** had a
129
+ value at the given path, and neither value was an Array or a Hash.
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
133
+ **reference** contained values that were not found in the **candidate**.
134
+
135
+ #### Paths
136
+
137
+ The second column contains a path expression. This is extremely lo-fi:
138
+
139
+ * The root of the data structure is defined as "/".
140
+ * If an element in the data structure is an array, its child elements will have
141
+ a **one-based** index appended to their parent's path.
142
+ * If an element in the data structure is an object ("Hash" in Ruby), the key
143
+ for each element will be appended to their parent's path, and the values will
144
+ be compared.
145
+
146
+ _**Being primarily a Ruby developer, I'm quite ignorant of conventions in the
147
+ JS community; if there's an existing convention for paths, please open an
148
+ issue!**_
149
+
150
+ #### Output Formats
28
151
 
29
- Create two JSON strings and pass them to `CheckPlease.render_diff`. You'll get
30
- back a third string containing a nicely formatted report of all the differences
31
- CheckPlease found in the two JSON strings. (See also: ./usage_examples.rb.)
152
+ CheckPlease produces tabular output by default. (It leans heavily on the
153
+ amazing [table_print](http://tableprintgem.com) gem for this.)
32
154
 
33
- (You can also parse the JSON strings yourself and pass the resulting data
34
- structures in, if you're into that. I mean, I wrote this to help compare JSON
35
- data that's too big and complicated to scan through visually, but you do you!
155
+ If you want to incorporate CheckPlease into some other toolchain, it can also
156
+ print diffs as JSON to facilitate parsing. In Ruby, pass `format: :json` to
157
+ `CheckPlease.render_diff`; in the CLI, use the `-f`/`--format` switch.
36
158
 
37
159
  ## TODO
38
160
 
39
- * rspec custom matcher (separate gem?)
40
161
  * command line flags for :allthethings:!
41
- * limit to first N
42
162
  * sort by path?
43
- * max depth (for iterative refinement?)
44
163
  * detect timestamps and compare after parsing?
45
164
  * ignore sub-second precision (option / CLI flag)?
46
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
- RSpec::Core::RakeTask.new(:spec)
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
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'check_please'
4
+ CheckPlease::CLI.run(__FILE__)
@@ -1,38 +1,44 @@
1
1
  require_relative "check_please/version"
2
+ require_relative "check_please/error"
2
3
  require_relative "check_please/path"
3
4
  require_relative "check_please/comparison"
4
5
  require_relative "check_please/diff"
5
6
  require_relative "check_please/diffs"
6
7
  require_relative "check_please/printers"
8
+ require_relative "check_please/cli"
9
+
10
+ require 'yaml'
11
+ require 'json'
7
12
 
8
13
  module CheckPlease
9
- class Error < StandardError; end
14
+ ELEVATOR_PITCH = "Tool for parsing and diffing two JSON documents."
10
15
 
11
- def self.diff(reference, candidate)
16
+ def self.diff(reference, candidate, options = {})
12
17
  reference = maybe_parse(reference)
13
18
  candidate = maybe_parse(candidate)
14
- Comparison.perform(reference, candidate)
19
+ Comparison.perform(reference, candidate, options)
15
20
  end
16
21
 
17
- def self.render_diff(reference, candidate, format: nil)
18
- diffs = diff(reference, candidate)
19
- Printers.render(diffs, format)
22
+ def self.render_diff(reference, candidate, options = {})
23
+ diffs = diff(reference, candidate, options)
24
+ Printers.render(diffs, options)
20
25
  end
21
26
 
22
27
  class << self
23
28
  private
24
29
 
25
30
  # Maybe you gave us JSON strings, maybe you gave us Ruby objects.
26
- # We just don't know! That's what makes it so exciting!
27
- def maybe_parse(maybe_json)
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)
28
34
 
29
- case maybe_json
30
- when String ; JSON.parse(maybe_json) # don't worry, if this raises we'll assume you've already parsed it
31
- else ; maybe_json
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
32
38
  end
33
39
 
34
- rescue JSON::ParserError
35
- return maybe_json
40
+ rescue JSON::ParserError, Psych::SyntaxError
41
+ return document
36
42
  end
37
43
  end
38
44
  end
@@ -0,0 +1,45 @@
1
+ require_relative 'cli/flag'
2
+ # require_relative 'cli/flags'
3
+ require_relative 'cli/parser'
4
+ require_relative 'cli/runner'
5
+
6
+ module CheckPlease
7
+
8
+ module CLI
9
+ def self.run(exe_file_name)
10
+ Runner.new(__FILE__).run(*ARGV.dup)
11
+ end
12
+
13
+
14
+
15
+ FLAGS = []
16
+ def self.flag(long:, short: nil, &block)
17
+ flag = Flag.new(short, long, &block)
18
+ FLAGS << flag
19
+ end
20
+
21
+ ##### Define CLI flags here #####
22
+
23
+ flag short: "-f FORMAT", long: "--format FORMAT" do |f|
24
+ f.desc = "format in which to present diffs (available options: [#{CheckPlease::Printers::FORMATS.join(", ")}])"
25
+ f.set_key :format, :to_sym
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
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,40 @@
1
+ module CheckPlease
2
+ module CLI
3
+
4
+ class Flag
5
+ ATTR_NAMES = %i[ short long desc key block ]
6
+ attr_accessor(*ATTR_NAMES)
7
+
8
+ def initialize(*args)
9
+ self.short = args.shift if args.any?
10
+ self.long = args.shift if args.any?
11
+
12
+ yield self if block_given?
13
+
14
+ missing = ATTR_NAMES.select { |e| self.send(e).nil? }
15
+ missing -= %i[ short ] # short is optional!
16
+ if missing.any?
17
+ raise ArgumentError, "Missing attributes: #{missing.join(', ')}"
18
+ end
19
+ end
20
+
21
+ def visit_option_parser(parser, options)
22
+ parser.on(short, long, desc) do |value|
23
+ block.call options, value
24
+ end
25
+ end
26
+
27
+ def set_key(key, message = nil, &b)
28
+ raise ArgumentError if message && b
29
+ raise ArgumentError if !message && !b
30
+
31
+ self.key = key
32
+ self.block = ->(options, value) {
33
+ b ||= message.to_sym.to_proc
34
+ options[key] = b.call(value)
35
+ }
36
+ end
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,58 @@
1
+ require 'optparse'
2
+
3
+ module CheckPlease
4
+ module CLI
5
+
6
+ class Parser
7
+ class UnrecognizedOption < StandardError
8
+ include CheckPlease::Error
9
+ end
10
+
11
+ def initialize(exe_file_name)
12
+ @exe_file_name = exe_file_name
13
+ @optparse = OptionParser.new
14
+ @optparse.banner = banner
15
+
16
+ @options = {} # yuck
17
+ CheckPlease::CLI::FLAGS.each do |flag|
18
+ flag.visit_option_parser(@optparse, @options)
19
+ end
20
+ end
21
+
22
+ # Unfortunately, OptionParser *really* wants to use closures.
23
+ # I haven't yet figured out how to get around this...
24
+ def consume_flags!(args)
25
+ @optparse.parse!(args) # removes recognized flags from `args`
26
+ return @options
27
+ rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption => e
28
+ raise UnrecognizedOption, e.message, cause: e
29
+ end
30
+
31
+ def help
32
+ @optparse.help
33
+ end
34
+
35
+ private
36
+
37
+ def banner
38
+ <<~EOF
39
+ Usage: #{@exe_file_name} <reference> <candidate> [FLAGS]
40
+
41
+ #{CheckPlease::ELEVATOR_PITCH}
42
+
43
+ Arguments:
44
+ <reference> is the name of a file to use as, well, the reference.
45
+ <candidate> is the name of a file to compare against the reference.
46
+
47
+ NOTE: If you have a utility like MacOS's `pbpaste`, you MAY omit
48
+ the <candidate> arg, and pipe the second document instead, like:
49
+
50
+ $ pbpaste | #{@exe_file_name} <reference>
51
+
52
+ FLAGS:
53
+ EOF
54
+ end
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,79 @@
1
+ module CheckPlease
2
+ module CLI
3
+
4
+ class Runner
5
+ def initialize(exe_file_name)
6
+ @parser = Parser.new(exe_file_name)
7
+ end
8
+
9
+ # NOTE: unusually for me, I'm using Ruby's `or` keyword in this method.
10
+ # `or` short circuits just like `||`, but has lower precedence, which
11
+ # enables some shenanigans...
12
+ def run(*args)
13
+ args.flatten!
14
+ print_help_and_exit if args.empty?
15
+
16
+ begin
17
+ options = @parser.consume_flags!(args)
18
+ rescue Parser::UnrecognizedOption => e
19
+ print_help_and_exit e.message
20
+ end
21
+
22
+ # The reference MUST be the first arg...
23
+ reference = \
24
+ read_file(args.shift) \
25
+ or print_help_and_exit "Missing <reference> argument"
26
+
27
+ # The candidate MAY be the second arg, or it might have been piped in...
28
+ candidate = \
29
+ read_file(args.shift) \
30
+ || read_piped_stdin \
31
+ or print_help_and_exit "Missing <candidate> argument, AND nothing was piped in"
32
+
33
+ # Looks like we're good to go!
34
+ diff_view = CheckPlease.render_diff(reference, candidate, options)
35
+ puts diff_view
36
+ end
37
+
38
+
39
+
40
+ private
41
+
42
+ def print_help_and_exit(message = nil)
43
+ puts "\n>>> #{message}\n\n" if message
44
+ puts @parser.help
45
+ exit
46
+ end
47
+
48
+ def read_file(filename)
49
+ return nil if filename.nil?
50
+ File.read(filename)
51
+ rescue Errno::ENOENT
52
+ return nil
53
+ end
54
+
55
+ # Unfortunately, ARGF won't help us here because it doesn't seem to want to
56
+ # read from stdin after it's already pulled a file out of ARGV. So, we
57
+ # have to read from stdin ourselves.
58
+ #
59
+ # BUT THAT'S NOT ALL! If the user didn't actually pipe any data,
60
+ # $stdin.read will block until they manually send EOF or hit Ctrl+C.
61
+ #
62
+ # Fortunately, we can detect whether $stdin.read will block by checking to
63
+ # see if it is a TTY. (Wait, what century is this again?)
64
+ #
65
+ # For fun and posterity, here's an experiment you can use to demonstrate this:
66
+ #
67
+ # $ ruby -e 'puts $stdin.tty? ? "YES YOU ARE A TTY" : "nope, no tty here"'
68
+ # YES YOU ARE A TTY
69
+ #
70
+ # $ cat foo | ruby -e 'puts $stdin.tty? ? "YES YOU ARE A TTY" : "nope, no tty here"'
71
+ # nope, no tty here
72
+ def read_piped_stdin
73
+ return nil if $stdin.tty?
74
+ return $stdin.read
75
+ end
76
+ end
77
+
78
+ end
79
+ 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
- compare reference, candidate, root, diffs
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
@@ -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
- def initialize(diff_list = nil)
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
@@ -0,0 +1,9 @@
1
+ module CheckPlease
2
+
3
+ module Error
4
+ # Rather than having a common error superclass, I'm taking a cue from
5
+ # https://avdi.codes/exceptionalruby and tagging things with a module
6
+ # instead....
7
+ end
8
+
9
+ end
@@ -11,6 +11,10 @@ module CheckPlease
11
11
  self.class.new( Array(@segments) + Array(new_basename.to_s) )
12
12
  end
13
13
 
14
+ def depth
15
+ 1 + @segments.length
16
+ end
17
+
14
18
  def to_s
15
19
  SEPARATOR + @segments.join(SEPARATOR)
16
20
  end
@@ -12,8 +12,8 @@ module CheckPlease
12
12
  FORMATS = PRINTERS_BY_FORMAT.keys.sort
13
13
  DEFAULT_FORMAT = :table
14
14
 
15
- def self.render(diffs, format)
16
- format ||= DEFAULT_FORMAT
15
+ def self.render(diffs, options = {})
16
+ format = options[:format] || DEFAULT_FORMAT
17
17
  printer = PRINTERS_BY_FORMAT[format.to_sym]
18
18
  printer.render(diffs)
19
19
  end
@@ -1,5 +1,3 @@
1
- require 'json'
2
-
3
1
  module CheckPlease
4
2
  module Printers
5
3
 
@@ -14,11 +14,12 @@ module Printers
14
14
  def to_s
15
15
  return "" if diffs.empty?
16
16
 
17
- build_string do |io|
17
+ out = build_string do |io|
18
18
  switch_tableprint_io(io) do
19
19
  tp diffs.data, *TP_OPTS
20
20
  end
21
21
  end
22
+ strip_trailing_whitespace(out)
22
23
  end
23
24
 
24
25
  private
@@ -31,6 +32,10 @@ module Printers
31
32
  ensure
32
33
  config.io = @old_io
33
34
  end
35
+
36
+ def strip_trailing_whitespace(s)
37
+ s.lines.map(&:rstrip).join("\n")
38
+ end
34
39
  end
35
40
 
36
41
  end
@@ -1,3 +1,5 @@
1
1
  module CheckPlease
2
- VERSION = "0.1.0"
2
+ # NOTE: 'check_please_rspec_matcher' depends on this,
3
+ # so try to keep them roughly in sync
4
+ VERSION = "0.2.4"
3
5
  end
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.1.0
4
+ version: 0.2.4
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-06 00:00:00.000000000 Z
11
+ date: 2020-11-18 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,14 +70,19 @@ 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
78
+ - lib/check_please/cli.rb
79
+ - lib/check_please/cli/flag.rb
80
+ - lib/check_please/cli/parser.rb
81
+ - lib/check_please/cli/runner.rb
77
82
  - lib/check_please/comparison.rb
78
83
  - lib/check_please/diff.rb
79
84
  - lib/check_please/diffs.rb
85
+ - lib/check_please/error.rb
80
86
  - lib/check_please/path.rb
81
87
  - lib/check_please/printers.rb
82
88
  - lib/check_please/printers/base.rb
@@ -1,70 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'optparse'
4
-
5
- require_relative '../lib/check_please'
6
-
7
- argv = ARGV.dup
8
-
9
- ref_file = argv.shift
10
- can_file = argv.shift
11
- diff_opts = {}
12
-
13
- @parser = OptionParser.new do |opts|
14
- opts.banner = <<~EOF
15
- Usage: #{__FILE__} <reference> <candidate> <options>
16
-
17
- Tool for parsing and diffing two JSON files.
18
-
19
- Arguments:
20
- <reference> is the name of a file to use as the reference.
21
- <candidate> is the name of a file to compare against the reference.
22
-
23
- NOTE: If the <candidate> arg is omitted, stdin will be used instead.
24
- This allows you to copy candidate JSON to the clipboard and (on a Mac) do:
25
-
26
- $ pbpaste | #{__FILE__} <reference>
27
-
28
- <options>:
29
- EOF
30
-
31
- formats = CheckPlease::Printers::FORMATS.join(", ")
32
-
33
- opts.on("-f FORMAT", "--format FORMAT", "specify the format (available options: [#{formats}]") do |val|
34
- diff_opts[:format] = val
35
- end
36
- end
37
-
38
-
39
-
40
- def print_help_and_exit
41
- @parser.parse(%w[--help])
42
- exit # technically redundant but helps me feel better
43
- end
44
-
45
- def read_file(filename)
46
- return nil if filename.to_s =~ /^\s*$/
47
- File.read(filename)
48
- rescue Errno::ENOENT
49
- # no such file, buddy
50
- return nil
51
- end
52
-
53
-
54
-
55
- # First off, try to read in the files the user told us about...
56
- reference = read_file(ref_file)
57
- candidate = read_file(can_file) || $stdin.read
58
-
59
- print_help_and_exit if reference.to_s =~ /^\s*$/
60
- print_help_and_exit if candidate.to_s =~ /^\s*$/
61
-
62
- begin
63
- @parser.parse(argv)
64
- rescue OptionParser::InvalidOption, OptionParser::AmbiguousOption => e
65
- puts "\n>>> #{e.message}\n\n"
66
- print_help_and_exit
67
- end
68
-
69
- report = CheckPlease.render_diff(reference, candidate, **diff_opts)
70
- puts report