check_please 0.1.0 → 0.2.4

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: 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