specdiff 0.1.0
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 +7 -0
- data/.rspec +3 -0
- data/.rubocop.yml +203 -0
- data/.tool-versions +1 -0
- data/CHANGELOG.md +5 -0
- data/Gemfile +12 -0
- data/Gemfile.lock +76 -0
- data/LICENSE.txt +21 -0
- data/README.md +84 -0
- data/Rakefile +12 -0
- data/examples/webmock/Gemfile +6 -0
- data/examples/webmock/Gemfile.lock +50 -0
- data/examples/webmock/json.rb +37 -0
- data/examples/webmock/text.rb +39 -0
- data/glossary.txt +24 -0
- data/lib/specdiff/colorize.rb +31 -0
- data/lib/specdiff/config.rb +27 -0
- data/lib/specdiff/diff.rb +27 -0
- data/lib/specdiff/differ/hashdiff.rb +85 -0
- data/lib/specdiff/differ/not_found.rb +24 -0
- data/lib/specdiff/differ/text.rb +73 -0
- data/lib/specdiff/differ.rb +100 -0
- data/lib/specdiff/plugin.rb +11 -0
- data/lib/specdiff/plugins/json.rb +42 -0
- data/lib/specdiff/plugins.rb +73 -0
- data/lib/specdiff/threadlocal.rb +8 -0
- data/lib/specdiff/version.rb +3 -0
- data/lib/specdiff/webmock/request_body_diff.rb +41 -0
- data/lib/specdiff/webmock.rb +1 -0
- data/lib/specdiff.rb +16 -0
- data/specdiff.gemspec +31 -0
- metadata +104 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 7da7ac63f4638bd752f83ff41a27488757e43e57c028891d4fd944afd03f58ca
|
|
4
|
+
data.tar.gz: d5a49e6aeb3525a454df920aa7d1b541a0812ffb9f8d0e1813a4bc05b57b006f
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 38ae83cd45a69892ca6d22915e0b253447eb12f42165a7673751abbb3993381702a02771ad3181a96d26204e6d171268a14423e85c2323a345df243be918f5a5
|
|
7
|
+
data.tar.gz: f44ce432951f3e2198ed6c4ed4cfc975b4eb201ced235791cd917c2476ca7a60fcb4ce0aff023fd4423f97b7eee2819d395d514b970188a5bc232ce89ee44ea6
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
AllCops:
|
|
2
|
+
TargetRubyVersion: 3.0
|
|
3
|
+
|
|
4
|
+
require:
|
|
5
|
+
- rubocop-rspec
|
|
6
|
+
- rubocop-rake
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
# LAYOUT COPS
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
Layout:
|
|
13
|
+
Enabled: false
|
|
14
|
+
|
|
15
|
+
Layout/LineLength:
|
|
16
|
+
Enabled: true
|
|
17
|
+
Max: 120
|
|
18
|
+
|
|
19
|
+
Layout/SpaceInsideBlockBraces:
|
|
20
|
+
Enabled: true
|
|
21
|
+
EnforcedStyle: space
|
|
22
|
+
SpaceBeforeBlockParameters: no_space
|
|
23
|
+
Layout/SpaceInsideHashLiteralBraces:
|
|
24
|
+
Enabled: true
|
|
25
|
+
EnforcedStyle: no_space
|
|
26
|
+
|
|
27
|
+
Layout/MultilineMethodCallIndentation:
|
|
28
|
+
Enabled: true
|
|
29
|
+
EnforcedStyle: indented
|
|
30
|
+
|
|
31
|
+
##
|
|
32
|
+
# STYLE COPS
|
|
33
|
+
##
|
|
34
|
+
|
|
35
|
+
Style:
|
|
36
|
+
Enabled: false
|
|
37
|
+
|
|
38
|
+
Style/TrailingCommaInArrayLiteral:
|
|
39
|
+
Enabled: true
|
|
40
|
+
EnforcedStyleForMultiline: consistent_comma
|
|
41
|
+
|
|
42
|
+
Style/TrailingCommaInHashLiteral:
|
|
43
|
+
Enabled: true
|
|
44
|
+
EnforcedStyleForMultiline: consistent_comma
|
|
45
|
+
|
|
46
|
+
Style/SpecialGlobalVars:
|
|
47
|
+
Enabled: true
|
|
48
|
+
EnforcedStyle: use_english_names
|
|
49
|
+
|
|
50
|
+
##
|
|
51
|
+
# NAMING COPS
|
|
52
|
+
##
|
|
53
|
+
|
|
54
|
+
Naming/BlockForwarding: # new in 1.24
|
|
55
|
+
Enabled: false
|
|
56
|
+
Naming/MethodParameterName:
|
|
57
|
+
Enabled: false
|
|
58
|
+
|
|
59
|
+
##
|
|
60
|
+
# SECURITY COPS
|
|
61
|
+
##
|
|
62
|
+
|
|
63
|
+
Security/CompoundHash: # new in 1.28
|
|
64
|
+
Enabled: true
|
|
65
|
+
Security/IoMethods: # new in 1.22
|
|
66
|
+
Enabled: true
|
|
67
|
+
|
|
68
|
+
##
|
|
69
|
+
# LINT COPS
|
|
70
|
+
##
|
|
71
|
+
|
|
72
|
+
Lint/SuppressedException:
|
|
73
|
+
AllowComments: true
|
|
74
|
+
Lint/AmbiguousAssignment: # new in 1.7
|
|
75
|
+
Enabled: true
|
|
76
|
+
Lint/AmbiguousOperatorPrecedence: # new in 1.21
|
|
77
|
+
Enabled: true
|
|
78
|
+
Lint/AmbiguousRange: # new in 1.19
|
|
79
|
+
Enabled: true
|
|
80
|
+
Lint/ConstantOverwrittenInRescue: # new in 1.31
|
|
81
|
+
Enabled: true
|
|
82
|
+
Lint/DeprecatedConstants: # new in 1.8
|
|
83
|
+
Enabled: true
|
|
84
|
+
Lint/DuplicateBranch: # new in 1.3
|
|
85
|
+
Enabled: true
|
|
86
|
+
IgnoreLiteralBranches: true
|
|
87
|
+
IgnoreConstantBranches: true
|
|
88
|
+
Lint/DuplicateMagicComment: # new in 1.37
|
|
89
|
+
Enabled: true
|
|
90
|
+
Lint/DuplicateMatchPattern: # new in 1.50
|
|
91
|
+
Enabled: true
|
|
92
|
+
Lint/DuplicateRegexpCharacterClassElement: # new in 1.1
|
|
93
|
+
Enabled: true
|
|
94
|
+
Lint/EmptyBlock: # new in 1.1
|
|
95
|
+
Enabled: true
|
|
96
|
+
Lint/EmptyClass: # new in 1.3
|
|
97
|
+
Enabled: true
|
|
98
|
+
Lint/EmptyInPattern: # new in 1.16
|
|
99
|
+
Enabled: true
|
|
100
|
+
Lint/IncompatibleIoSelectWithFiberScheduler: # new in 1.21
|
|
101
|
+
Enabled: true
|
|
102
|
+
Lint/LambdaWithoutLiteralBlock: # new in 1.8
|
|
103
|
+
Enabled: true
|
|
104
|
+
Lint/MixedCaseRange: # new in 1.53
|
|
105
|
+
Enabled: true
|
|
106
|
+
Lint/NoReturnInBeginEndBlocks: # new in 1.2
|
|
107
|
+
Enabled: true
|
|
108
|
+
Lint/NonAtomicFileOperation: # new in 1.31
|
|
109
|
+
Enabled: true
|
|
110
|
+
Lint/NumberedParameterAssignment: # new in 1.9
|
|
111
|
+
Enabled: true
|
|
112
|
+
Lint/OrAssignmentToConstant: # new in 1.9
|
|
113
|
+
Enabled: true
|
|
114
|
+
Lint/RedundantDirGlobSort: # new in 1.8
|
|
115
|
+
Enabled: true
|
|
116
|
+
Lint/RedundantRegexpQuantifiers: # new in 1.53
|
|
117
|
+
Enabled: true
|
|
118
|
+
Lint/RefinementImportMethods: # new in 1.27
|
|
119
|
+
Enabled: true
|
|
120
|
+
Lint/RequireRangeParentheses: # new in 1.32
|
|
121
|
+
Enabled: true
|
|
122
|
+
Lint/RequireRelativeSelfPath: # new in 1.22
|
|
123
|
+
Enabled: true
|
|
124
|
+
Lint/SymbolConversion: # new in 1.9
|
|
125
|
+
Enabled: true
|
|
126
|
+
Lint/ToEnumArguments: # new in 1.1
|
|
127
|
+
Enabled: true
|
|
128
|
+
Lint/TripleQuotes: # new in 1.9
|
|
129
|
+
Enabled: true
|
|
130
|
+
Lint/UnexpectedBlockArity: # new in 1.5
|
|
131
|
+
Enabled: true
|
|
132
|
+
Lint/UnmodifiedReduceAccumulator: # new in 1.1
|
|
133
|
+
Enabled: true
|
|
134
|
+
Lint/UselessRescue: # new in 1.43
|
|
135
|
+
Enabled: true
|
|
136
|
+
Lint/UselessRuby2Keywords: # new in 1.23
|
|
137
|
+
Enabled: true
|
|
138
|
+
|
|
139
|
+
##
|
|
140
|
+
# METRICS COPS
|
|
141
|
+
##
|
|
142
|
+
|
|
143
|
+
Metrics:
|
|
144
|
+
Enabled: false
|
|
145
|
+
|
|
146
|
+
##
|
|
147
|
+
# GEMSPEC COPS
|
|
148
|
+
##
|
|
149
|
+
|
|
150
|
+
Gemspec/DeprecatedAttributeAssignment: # new in 1.30
|
|
151
|
+
Enabled: true
|
|
152
|
+
Gemspec/DevelopmentDependencies: # new in 1.44
|
|
153
|
+
Enabled: true
|
|
154
|
+
Gemspec/RequireMFA: # new in 1.23
|
|
155
|
+
Enabled: false
|
|
156
|
+
Gemspec/OrderedDependencies:
|
|
157
|
+
Enabled: false
|
|
158
|
+
|
|
159
|
+
##
|
|
160
|
+
# BUNDLER COPS
|
|
161
|
+
##
|
|
162
|
+
|
|
163
|
+
Bundler/OrderedGems:
|
|
164
|
+
Enabled: false
|
|
165
|
+
|
|
166
|
+
##
|
|
167
|
+
# RSPEC COPS
|
|
168
|
+
##
|
|
169
|
+
|
|
170
|
+
# documentation can be found in
|
|
171
|
+
# http://www.rubydoc.info/gems/rubocop-rspec/RuboCop/Cop/RSpec/<name>
|
|
172
|
+
RSpec:
|
|
173
|
+
Enabled: false
|
|
174
|
+
|
|
175
|
+
RSpec/HookArgument:
|
|
176
|
+
Enabled: true
|
|
177
|
+
EnforcedStyle: each
|
|
178
|
+
RSpec/MessageSpies:
|
|
179
|
+
Enabled: true
|
|
180
|
+
EnforcedStyle: receive
|
|
181
|
+
|
|
182
|
+
##
|
|
183
|
+
# CAPYBARA COPS
|
|
184
|
+
##
|
|
185
|
+
|
|
186
|
+
# cant remember installing capybara but whatever
|
|
187
|
+
Capybara:
|
|
188
|
+
Enabled: false
|
|
189
|
+
|
|
190
|
+
##
|
|
191
|
+
# FACTORYBOT COPS
|
|
192
|
+
##
|
|
193
|
+
|
|
194
|
+
# cant remember installing factorybot but whatever
|
|
195
|
+
FactoryBot:
|
|
196
|
+
Enabled: false
|
|
197
|
+
|
|
198
|
+
##
|
|
199
|
+
# RAKE COPS
|
|
200
|
+
##
|
|
201
|
+
|
|
202
|
+
Rake:
|
|
203
|
+
Enabled: true
|
data/.tool-versions
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ruby 3.2.2
|
data/CHANGELOG.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: .
|
|
3
|
+
specs:
|
|
4
|
+
specdiff (0.1.0)
|
|
5
|
+
diff-lcs (~> 1.5)
|
|
6
|
+
hashdiff (~> 1.0)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
ast (2.4.2)
|
|
12
|
+
diff-lcs (1.5.0)
|
|
13
|
+
hashdiff (1.0.1)
|
|
14
|
+
json (2.6.3)
|
|
15
|
+
language_server-protocol (3.17.0.3)
|
|
16
|
+
parallel (1.23.0)
|
|
17
|
+
parser (3.2.2.4)
|
|
18
|
+
ast (~> 2.4.1)
|
|
19
|
+
racc
|
|
20
|
+
racc (1.7.3)
|
|
21
|
+
rainbow (3.1.1)
|
|
22
|
+
rake (13.1.0)
|
|
23
|
+
regexp_parser (2.8.2)
|
|
24
|
+
rexml (3.2.6)
|
|
25
|
+
rspec (3.12.0)
|
|
26
|
+
rspec-core (~> 3.12.0)
|
|
27
|
+
rspec-expectations (~> 3.12.0)
|
|
28
|
+
rspec-mocks (~> 3.12.0)
|
|
29
|
+
rspec-core (3.12.2)
|
|
30
|
+
rspec-support (~> 3.12.0)
|
|
31
|
+
rspec-expectations (3.12.3)
|
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
33
|
+
rspec-support (~> 3.12.0)
|
|
34
|
+
rspec-mocks (3.12.6)
|
|
35
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
|
36
|
+
rspec-support (~> 3.12.0)
|
|
37
|
+
rspec-support (3.12.1)
|
|
38
|
+
rubocop (1.57.2)
|
|
39
|
+
json (~> 2.3)
|
|
40
|
+
language_server-protocol (>= 3.17.0)
|
|
41
|
+
parallel (~> 1.10)
|
|
42
|
+
parser (>= 3.2.2.4)
|
|
43
|
+
rainbow (>= 2.2.2, < 4.0)
|
|
44
|
+
regexp_parser (>= 1.8, < 3.0)
|
|
45
|
+
rexml (>= 3.2.5, < 4.0)
|
|
46
|
+
rubocop-ast (>= 1.28.1, < 2.0)
|
|
47
|
+
ruby-progressbar (~> 1.7)
|
|
48
|
+
unicode-display_width (>= 2.4.0, < 3.0)
|
|
49
|
+
rubocop-ast (1.30.0)
|
|
50
|
+
parser (>= 3.2.1.0)
|
|
51
|
+
rubocop-capybara (2.19.0)
|
|
52
|
+
rubocop (~> 1.41)
|
|
53
|
+
rubocop-factory_bot (2.24.0)
|
|
54
|
+
rubocop (~> 1.33)
|
|
55
|
+
rubocop-rake (0.6.0)
|
|
56
|
+
rubocop (~> 1.0)
|
|
57
|
+
rubocop-rspec (2.25.0)
|
|
58
|
+
rubocop (~> 1.40)
|
|
59
|
+
rubocop-capybara (~> 2.17)
|
|
60
|
+
rubocop-factory_bot (~> 2.22)
|
|
61
|
+
ruby-progressbar (1.13.0)
|
|
62
|
+
unicode-display_width (2.5.0)
|
|
63
|
+
|
|
64
|
+
PLATFORMS
|
|
65
|
+
x86_64-linux
|
|
66
|
+
|
|
67
|
+
DEPENDENCIES
|
|
68
|
+
rake (~> 13.0)
|
|
69
|
+
rspec (~> 3.0)
|
|
70
|
+
rubocop (~> 1.21)
|
|
71
|
+
rubocop-rake (~> 0.6.0)
|
|
72
|
+
rubocop-rspec (~> 2.25)
|
|
73
|
+
specdiff!
|
|
74
|
+
|
|
75
|
+
BUNDLED WITH
|
|
76
|
+
2.3.5
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Odin H B
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
# Specdiff
|
|
2
|
+
|
|
3
|
+
A gem for improving diff output in webmock.
|
|
4
|
+
|
|
5
|
+
Webmock currently has a somewhat hidden feature where it will produce a diff
|
|
6
|
+
between a request body and a registered stub when making an unregistered request
|
|
7
|
+
if they happen to both be json (including setting content type). This gem aims
|
|
8
|
+
to bring text diffing (ala rspec) to webmock via monkey-patch, as well as
|
|
9
|
+
dropping the content type requirement.
|
|
10
|
+
|
|
11
|
+
Specdiff automagically detects the types of provided data and prints a suitable
|
|
12
|
+
diff between them.
|
|
13
|
+
|
|
14
|
+
Check out the examples directory to see what it might look like.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
Add this line to your application's Gemfile:
|
|
19
|
+
|
|
20
|
+
```ruby
|
|
21
|
+
gem "specdiff", require: false
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
And then execute:
|
|
25
|
+
|
|
26
|
+
$ bundle install
|
|
27
|
+
|
|
28
|
+
Or install it yourself as:
|
|
29
|
+
|
|
30
|
+
$ gem install specdiff
|
|
31
|
+
|
|
32
|
+
## Usage
|
|
33
|
+
|
|
34
|
+
Put the following in your `spec_helper.rb`. (Or equivalent initializer
|
|
35
|
+
for test environment) You probably don't want to load/use this gem in a release
|
|
36
|
+
environment.
|
|
37
|
+
|
|
38
|
+
```rb
|
|
39
|
+
# spec_helper.rb
|
|
40
|
+
|
|
41
|
+
require "specdiff"
|
|
42
|
+
require "specdiff/webmock" # optional, webmock patches
|
|
43
|
+
|
|
44
|
+
# optionally, you can turn off terminal colors
|
|
45
|
+
Specdiff.configure do |config|
|
|
46
|
+
config.colorize = true
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
The webmock patch should make webmock show diffs from the specdiff gem when
|
|
51
|
+
stubs mismatch.
|
|
52
|
+
|
|
53
|
+
### Direct usage
|
|
54
|
+
|
|
55
|
+
You can also use the gem directly:
|
|
56
|
+
|
|
57
|
+
```rb
|
|
58
|
+
diff = Specdiff.diff(something, and_something_else)
|
|
59
|
+
|
|
60
|
+
diff.empty? # => true/false, if it is empty you might want to not print the diff, it is probably useless
|
|
61
|
+
diff.to_s # => a string for showing to a developer who may or may not be scratching their head
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
## Development
|
|
65
|
+
|
|
66
|
+
Install the software versions specified in `.tool-versions`.
|
|
67
|
+
|
|
68
|
+
Run `bin/setup` to install dependencies. Then, run `bundle exec rake` to run the tests and linter and make sure they're green before starting to make your changes.
|
|
69
|
+
|
|
70
|
+
Run `bundle exec rake -AD` gor a full list of all the available tasks you may use for development purposes.
|
|
71
|
+
|
|
72
|
+
You can also run `bin/console` for an interactive prompt that will allow you to experiment with the gem code loaded.
|
|
73
|
+
|
|
74
|
+
## Releasing
|
|
75
|
+
|
|
76
|
+
To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
|
|
80
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/odinhb/specdiff.
|
|
81
|
+
|
|
82
|
+
## License
|
|
83
|
+
|
|
84
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
PATH
|
|
2
|
+
remote: ../..
|
|
3
|
+
specs:
|
|
4
|
+
specdiff (0.1.0)
|
|
5
|
+
diff-lcs (~> 1.5)
|
|
6
|
+
hashdiff (~> 1.0)
|
|
7
|
+
|
|
8
|
+
GEM
|
|
9
|
+
remote: https://rubygems.org/
|
|
10
|
+
specs:
|
|
11
|
+
addressable (2.8.5)
|
|
12
|
+
public_suffix (>= 2.0.2, < 6.0)
|
|
13
|
+
crack (0.4.5)
|
|
14
|
+
rexml
|
|
15
|
+
diff-lcs (1.5.0)
|
|
16
|
+
domain_name (0.6.20231109)
|
|
17
|
+
ffi (1.16.3)
|
|
18
|
+
ffi-compiler (1.0.1)
|
|
19
|
+
ffi (>= 1.0.0)
|
|
20
|
+
rake
|
|
21
|
+
hashdiff (1.0.1)
|
|
22
|
+
http (5.1.1)
|
|
23
|
+
addressable (~> 2.8)
|
|
24
|
+
http-cookie (~> 1.0)
|
|
25
|
+
http-form_data (~> 2.2)
|
|
26
|
+
llhttp-ffi (~> 0.4.0)
|
|
27
|
+
http-cookie (1.0.5)
|
|
28
|
+
domain_name (~> 0.5)
|
|
29
|
+
http-form_data (2.3.0)
|
|
30
|
+
llhttp-ffi (0.4.0)
|
|
31
|
+
ffi-compiler (~> 1.0)
|
|
32
|
+
rake (~> 13.0)
|
|
33
|
+
public_suffix (5.0.4)
|
|
34
|
+
rake (13.1.0)
|
|
35
|
+
rexml (3.2.6)
|
|
36
|
+
webmock (3.19.1)
|
|
37
|
+
addressable (>= 2.8.0)
|
|
38
|
+
crack (>= 0.3.2)
|
|
39
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
|
40
|
+
|
|
41
|
+
PLATFORMS
|
|
42
|
+
x86_64-linux
|
|
43
|
+
|
|
44
|
+
DEPENDENCIES
|
|
45
|
+
http
|
|
46
|
+
specdiff!
|
|
47
|
+
webmock
|
|
48
|
+
|
|
49
|
+
BUNDLED WITH
|
|
50
|
+
2.3.5
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
require "webmock"
|
|
2
|
+
require "specdiff"
|
|
3
|
+
require "specdiff/webmock"
|
|
4
|
+
|
|
5
|
+
Specdiff.load!(:json)
|
|
6
|
+
|
|
7
|
+
include WebMock::API
|
|
8
|
+
|
|
9
|
+
WebMock.enable!
|
|
10
|
+
WebMock.show_body_diff! # on by default
|
|
11
|
+
|
|
12
|
+
stub_request(:post, "https://www.example.com")
|
|
13
|
+
.with(
|
|
14
|
+
body: JSON.generate({
|
|
15
|
+
my_hash: "is great",
|
|
16
|
+
the_hash: "is amazing",
|
|
17
|
+
})
|
|
18
|
+
)
|
|
19
|
+
.to_return(status: 400, body: "hello")
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
HTTP.post(
|
|
23
|
+
"https://www.example.com",
|
|
24
|
+
body: JSON.generate({
|
|
25
|
+
i_had_to_go: "and post a different hash",
|
|
26
|
+
my_hash: "is different",
|
|
27
|
+
}),
|
|
28
|
+
)
|
|
29
|
+
rescue WebMock::NetConnectNotAllowedError => e
|
|
30
|
+
puts "success! webmock stopped the request"
|
|
31
|
+
puts "here is the error text:\n\n"
|
|
32
|
+
|
|
33
|
+
puts e.message
|
|
34
|
+
exit 0
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
puts "nothing was raised??"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
require "webmock"
|
|
2
|
+
require "specdiff"
|
|
3
|
+
require "specdiff/webmock"
|
|
4
|
+
|
|
5
|
+
include WebMock::API
|
|
6
|
+
|
|
7
|
+
WebMock.enable!
|
|
8
|
+
WebMock.show_body_diff! # on by default
|
|
9
|
+
|
|
10
|
+
stub_request(:post, "https://www.example.com")
|
|
11
|
+
.with(
|
|
12
|
+
body: <<~TEXT1,
|
|
13
|
+
this is the expected body
|
|
14
|
+
that you should send
|
|
15
|
+
nothing less
|
|
16
|
+
nothing more
|
|
17
|
+
TEXT1
|
|
18
|
+
)
|
|
19
|
+
.to_return(status: 400, body: "hello")
|
|
20
|
+
|
|
21
|
+
begin
|
|
22
|
+
HTTP.post(
|
|
23
|
+
"https://www.example.com",
|
|
24
|
+
body: <<~TEXT2,
|
|
25
|
+
this is the unexpected body
|
|
26
|
+
that i should not have sent
|
|
27
|
+
nothing less
|
|
28
|
+
nothing more
|
|
29
|
+
TEXT2
|
|
30
|
+
)
|
|
31
|
+
rescue WebMock::NetConnectNotAllowedError => e
|
|
32
|
+
puts "success! webmock stopped the request"
|
|
33
|
+
puts "here is the error text:\n\n"
|
|
34
|
+
|
|
35
|
+
puts e.message
|
|
36
|
+
exit 0
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
puts "nothing was raised??"
|
data/glossary.txt
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
diff
|
|
2
|
+
the return value from the Specdiff::diff method.
|
|
3
|
+
this is not the direct return value from a plugin/differ, that is the
|
|
4
|
+
"raw diff" (or diff.raw)
|
|
5
|
+
|
|
6
|
+
differ
|
|
7
|
+
a class responding to ::diff(a, b) and ::stringify(diff) able to produce a
|
|
8
|
+
human-comprehensible diff output for your terminal
|
|
9
|
+
|
|
10
|
+
plugin
|
|
11
|
+
external differ (responding to more methods), able to be provided by third
|
|
12
|
+
parties or end users themselves
|
|
13
|
+
|
|
14
|
+
type
|
|
15
|
+
a symbol like :text, :json or :hash which denotes the type of data in a way
|
|
16
|
+
which is useful for picking a differ
|
|
17
|
+
|
|
18
|
+
:text
|
|
19
|
+
a string which likely contains plaintext data of some kind
|
|
20
|
+
|
|
21
|
+
side
|
|
22
|
+
an object containing either side of a comparison that needs to be made
|
|
23
|
+
when defining a plugin, the diff method receives two sides: a, b
|
|
24
|
+
most importantly, a side contains a value and a type
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
module Specdiff::Colorize
|
|
2
|
+
module_function
|
|
3
|
+
|
|
4
|
+
def colorize_by_line(string, line_separator = "\n")
|
|
5
|
+
return string if !::Specdiff.config.colorize?
|
|
6
|
+
|
|
7
|
+
string.lines(line_separator).map do |line|
|
|
8
|
+
yield(line)
|
|
9
|
+
end.join
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def reset_color(text)
|
|
13
|
+
"\e[0m#{text}"
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def red(text)
|
|
17
|
+
"\e[31m#{text}\e[0m"
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def green(text)
|
|
21
|
+
"\e[32m#{text}\e[0m"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def yellow(text)
|
|
25
|
+
"\e[33m#{text}\e[0m"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def blue(text)
|
|
29
|
+
"\e[34m#{text}\e[0m"
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
module Specdiff
|
|
2
|
+
Config = Struct.new(:colorize, keyword_init: true) do
|
|
3
|
+
def colorize?
|
|
4
|
+
colorize
|
|
5
|
+
end
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
# Read the configuration
|
|
9
|
+
def self.config
|
|
10
|
+
threadlocal[:config] ||= default_configuration
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# private, used for testing
|
|
14
|
+
def self._set_config(new_config)
|
|
15
|
+
threadlocal[:config] = new_config
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Set the configuration
|
|
19
|
+
def self.configure
|
|
20
|
+
yield(config)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Generates the default configuration
|
|
24
|
+
def self.default_configuration
|
|
25
|
+
Config.new(colorize: true)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
::Specdiff::Diff = Struct.new(
|
|
2
|
+
:differ, :a, :b, :raw, keyword_init: true,
|
|
3
|
+
) do
|
|
4
|
+
def differ_id
|
|
5
|
+
differ.id
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def to_s
|
|
9
|
+
differ.stringify(self)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def empty?
|
|
13
|
+
differ == ::Specdiff::Differ::NotFound
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def types
|
|
17
|
+
[a.type, b.type]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def inspect
|
|
21
|
+
if empty?
|
|
22
|
+
"<Specdiff::Diff (empty)>"
|
|
23
|
+
else
|
|
24
|
+
"<Specdiff::Diff w/ #{raw&.bytesize || 0} bytes of #raw diff>"
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
require "hashdiff"
|
|
2
|
+
require "pp"
|
|
3
|
+
|
|
4
|
+
class Specdiff::Differ::Hashdiff
|
|
5
|
+
extend ::Specdiff::Colorize
|
|
6
|
+
|
|
7
|
+
def self.diff(a, b)
|
|
8
|
+
# array_path: true returns the path as an array, which differentiates
|
|
9
|
+
# between symbol keys and string keys in hashes, while the string
|
|
10
|
+
# representation does not.
|
|
11
|
+
# hmm it really seems like use_lcs: true gives much less human-readable
|
|
12
|
+
# (human-comprehensible) output when arrays are involved.
|
|
13
|
+
Hashdiff.diff(
|
|
14
|
+
a.value, b.value,
|
|
15
|
+
array_path: true,
|
|
16
|
+
use_lcs: false,
|
|
17
|
+
)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def self.stringify(diff)
|
|
21
|
+
diff.raw.pretty_inspect
|
|
22
|
+
|
|
23
|
+
result = +""
|
|
24
|
+
|
|
25
|
+
diff.raw.each do |change|
|
|
26
|
+
type = change[0] # the char '+', '-' or '~'
|
|
27
|
+
path = change[1] # for example key1.key2[2] (as ["key1", :key2, 2])
|
|
28
|
+
path2 = _stringify_path(path)
|
|
29
|
+
|
|
30
|
+
if type == "+"
|
|
31
|
+
value = change[2]
|
|
32
|
+
|
|
33
|
+
result << "added #{path2} with value #{value.inspect}"
|
|
34
|
+
elsif type == "-"
|
|
35
|
+
value = change[2]
|
|
36
|
+
|
|
37
|
+
result << "removed #{path2} with value #{value.inspect}"
|
|
38
|
+
elsif type == "~"
|
|
39
|
+
from = change[2]
|
|
40
|
+
to = change[3]
|
|
41
|
+
|
|
42
|
+
result << "changed #{path2} from #{from.inspect} to #{to.inspect}"
|
|
43
|
+
else
|
|
44
|
+
result << change.inspect
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
result << "\n"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
colorize_by_line(result) do |line|
|
|
51
|
+
if line.start_with?("removed")
|
|
52
|
+
red(line)
|
|
53
|
+
elsif line.start_with?("added")
|
|
54
|
+
green(line)
|
|
55
|
+
elsif line.start_with?("changed")
|
|
56
|
+
yellow(line)
|
|
57
|
+
else
|
|
58
|
+
reset_color(line)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
PATH_SEPARATOR = ".".freeze
|
|
64
|
+
|
|
65
|
+
def self._stringify_path(path)
|
|
66
|
+
result = +""
|
|
67
|
+
|
|
68
|
+
path.each do |component|
|
|
69
|
+
if component.is_a?(Numeric)
|
|
70
|
+
result.chomp!(PATH_SEPARATOR)
|
|
71
|
+
result << "[#{component}]"
|
|
72
|
+
elsif component.is_a?(Symbol)
|
|
73
|
+
# by not inspecting symbols they look prettier than strings, but you
|
|
74
|
+
# can still tell the difference in the printed output
|
|
75
|
+
result << component.to_s
|
|
76
|
+
else
|
|
77
|
+
result << component.inspect
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
result << PATH_SEPARATOR
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
result.chomp(PATH_SEPARATOR)
|
|
84
|
+
end
|
|
85
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# this is the null differ
|
|
2
|
+
class Specdiff::Differ::NotFound
|
|
3
|
+
def self.diff(a, b)
|
|
4
|
+
comparison = "!="
|
|
5
|
+
comparison = "=" if a.value == b.value
|
|
6
|
+
|
|
7
|
+
a_representation = _representation_for(a)
|
|
8
|
+
b_representation = _representation_for(b)
|
|
9
|
+
|
|
10
|
+
"#{a_representation} #{comparison} #{b_representation}"
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def self._representation_for(side)
|
|
14
|
+
if side.type == :binary
|
|
15
|
+
"<binary content>"
|
|
16
|
+
else
|
|
17
|
+
side.value.inspect
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def self.stringify(diff)
|
|
22
|
+
diff.raw
|
|
23
|
+
end
|
|
24
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
require "diff/lcs"
|
|
2
|
+
require "diff/lcs/hunk"
|
|
3
|
+
|
|
4
|
+
class Specdiff::Differ::Text
|
|
5
|
+
extend ::Specdiff::Colorize
|
|
6
|
+
|
|
7
|
+
NEWLINE = "\n".freeze
|
|
8
|
+
CONTEXT_LINES = 3
|
|
9
|
+
|
|
10
|
+
# this implementation is based on RSpec::Support::Differ
|
|
11
|
+
# https://github.com/rspec/rspec-support/blob/main/lib/rspec/support/differ.rb
|
|
12
|
+
# and also the hunk generator it uses
|
|
13
|
+
def self.diff(a, b)
|
|
14
|
+
a_value = a.value
|
|
15
|
+
b_value = b.value
|
|
16
|
+
|
|
17
|
+
if a_value.encoding != b_value.encoding
|
|
18
|
+
return <<~MSG
|
|
19
|
+
Strings have different encodings:
|
|
20
|
+
#{a.value.encoding.inspect} != #{b.value.encoding.inspect}
|
|
21
|
+
MSG
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
diff = ""
|
|
25
|
+
|
|
26
|
+
a_lines = a_value.split(NEWLINE).map! { _1.chomp }
|
|
27
|
+
b_lines = b_value.split(NEWLINE).map! { _1.chomp }
|
|
28
|
+
hunks = ::Diff::LCS.diff(a_lines, b_lines).map do |piece|
|
|
29
|
+
::Diff::LCS::Hunk.new(
|
|
30
|
+
a_lines, b_lines, piece, CONTEXT_LINES, 0,
|
|
31
|
+
)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
hunks.each_cons(2) do |prev_hunk, current_hunk|
|
|
35
|
+
begin
|
|
36
|
+
if current_hunk.overlaps?(prev_hunk)
|
|
37
|
+
current_hunk.merge(prev_hunk)
|
|
38
|
+
else
|
|
39
|
+
diff << prev_hunk.diff(:unified).to_s
|
|
40
|
+
end
|
|
41
|
+
ensure
|
|
42
|
+
diff << NEWLINE
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if hunks.last
|
|
47
|
+
diff << hunks.last.diff(:unified).to_s
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
diff << "\n"
|
|
51
|
+
|
|
52
|
+
return colorize_by_line(diff) do |line|
|
|
53
|
+
case line[0].chr
|
|
54
|
+
when "+"
|
|
55
|
+
green(line)
|
|
56
|
+
when "-"
|
|
57
|
+
red(line)
|
|
58
|
+
when "@"
|
|
59
|
+
if line[1].chr == "@"
|
|
60
|
+
blue(line)
|
|
61
|
+
else
|
|
62
|
+
reset_color(line)
|
|
63
|
+
end
|
|
64
|
+
else
|
|
65
|
+
reset_color(line)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def self.stringify(diff)
|
|
71
|
+
diff.raw
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
class Specdiff::Differ
|
|
2
|
+
Side = Struct.new(:value, :type, keyword_init: true)
|
|
3
|
+
|
|
4
|
+
def self.call(...)
|
|
5
|
+
new.call(...)
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def call(raw_a, raw_b)
|
|
9
|
+
a = parse_side(raw_a)
|
|
10
|
+
b = parse_side(raw_b)
|
|
11
|
+
|
|
12
|
+
if a.type == :text && b.type == :binary
|
|
13
|
+
new_b = try_reencode(b.value, a.value.encoding)
|
|
14
|
+
if new_b
|
|
15
|
+
b = b.dup
|
|
16
|
+
b.type = :text
|
|
17
|
+
b.value = new_b
|
|
18
|
+
end
|
|
19
|
+
elsif a.type == :binary && b.type == :text
|
|
20
|
+
new_a = try_reencode(a.value, b.value.encoding)
|
|
21
|
+
if new_a
|
|
22
|
+
a = a.dup
|
|
23
|
+
a.type = :text
|
|
24
|
+
a.value = new_a
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
differ = pick_differ(a, b)
|
|
29
|
+
raw = differ.diff(a, b)
|
|
30
|
+
|
|
31
|
+
if raw.is_a?(::Specdiff::Diff) # detect recursive plugins, such as json
|
|
32
|
+
raw
|
|
33
|
+
else
|
|
34
|
+
::Specdiff::Diff.new(raw: raw, differ: differ, a: a, b: b)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def parse_side(raw_value)
|
|
41
|
+
type = detect_type(raw_value)
|
|
42
|
+
|
|
43
|
+
Side.new(value: raw_value, type: type)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def detect_type(thing)
|
|
47
|
+
if (type = detect_plugin_types(thing))
|
|
48
|
+
type
|
|
49
|
+
elsif thing.is_a?(Hash)
|
|
50
|
+
:hash
|
|
51
|
+
elsif thing.is_a?(Array)
|
|
52
|
+
:array
|
|
53
|
+
elsif thing.is_a?(String) && thing.encoding == Encoding::BINARY
|
|
54
|
+
:binary
|
|
55
|
+
elsif thing.is_a?(String)
|
|
56
|
+
:text
|
|
57
|
+
elsif thing.nil?
|
|
58
|
+
:nil
|
|
59
|
+
else
|
|
60
|
+
:unknown
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def detect_plugin_types(thing)
|
|
65
|
+
Specdiff.plugins
|
|
66
|
+
.filter { |plugin| plugin.respond_to?(:detect_type) }
|
|
67
|
+
.detect { |plugin| plugin.detect_type(thing) }
|
|
68
|
+
&.id
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def try_reencode(binary_string, target_encoding)
|
|
72
|
+
binary_string.encode(target_encoding)
|
|
73
|
+
rescue StandardError
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def pick_differ(a, b)
|
|
78
|
+
if (differ = pick_plugin_differ(a, b))
|
|
79
|
+
differ
|
|
80
|
+
elsif a.type == :text && b.type == :text
|
|
81
|
+
Specdiff::Differ::Text
|
|
82
|
+
elsif a.type == :hash && b.type == :hash
|
|
83
|
+
Specdiff::Differ::Hashdiff
|
|
84
|
+
elsif a.type == :array && b.type == :array
|
|
85
|
+
Specdiff::Differ::Hashdiff
|
|
86
|
+
else
|
|
87
|
+
Specdiff::Differ::NotFound
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def pick_plugin_differ(a, b)
|
|
92
|
+
Specdiff.plugins
|
|
93
|
+
.detect { |plugin| plugin.compatible?(a, b) }
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# require only the builtin differs, plugins are optionally loaded later
|
|
98
|
+
require_relative "differ/not_found"
|
|
99
|
+
require_relative "differ/text"
|
|
100
|
+
require_relative "differ/hashdiff"
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require "json"
|
|
2
|
+
|
|
3
|
+
class Specdiff::Plugins::Json
|
|
4
|
+
include ::Specdiff::Plugin
|
|
5
|
+
|
|
6
|
+
def self.id
|
|
7
|
+
:json
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def self.detect_type(thing)
|
|
11
|
+
thing.is_a?(String) && _json_parsable?(thing)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self._json_parsable?(thing)
|
|
15
|
+
return false unless thing.is_a?(String)
|
|
16
|
+
|
|
17
|
+
JSON.parse(thing)
|
|
18
|
+
true
|
|
19
|
+
rescue JSON::ParserError
|
|
20
|
+
false
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.compatible?(a, b)
|
|
24
|
+
a.type == :json || b.type == :json
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def self.diff(a, b)
|
|
28
|
+
a_value = a.value
|
|
29
|
+
b_value = b.value
|
|
30
|
+
|
|
31
|
+
a_value = JSON.parse(a_value) if a.type == :json
|
|
32
|
+
b_value = JSON.parse(b_value) if b.type == :json
|
|
33
|
+
|
|
34
|
+
::Specdiff.diff(a_value, b_value)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.stringify(_diff)
|
|
38
|
+
# since we recurse back into Specdiff, we don't need to stringify in this
|
|
39
|
+
# plugin. the built in hash/array/text differs should do the stringification
|
|
40
|
+
raise "#{self.class}::stringify was called, this should never happen"
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
module Specdiff
|
|
2
|
+
THREADLOCAL_PLUGINS_KEY = :__specdiff_plugins
|
|
3
|
+
|
|
4
|
+
def self.plugins
|
|
5
|
+
threadlocal[THREADLOCAL_PLUGINS_KEY]
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
BUILTIN_PLUGINS = %i[json]
|
|
9
|
+
BUILTIN_TYPES = %i[hash array binary text nil]
|
|
10
|
+
|
|
11
|
+
# Load extra type support, such as support for json strings
|
|
12
|
+
def self.load!(*plugins)
|
|
13
|
+
return if plugins.size == 0
|
|
14
|
+
|
|
15
|
+
plugins.each do |new_plugin|
|
|
16
|
+
if BUILTIN_PLUGINS.include?(new_plugin)
|
|
17
|
+
case new_plugin
|
|
18
|
+
when :json
|
|
19
|
+
require "#{__dir__}/plugins/json"
|
|
20
|
+
load_plugin_class!(::Specdiff::Plugins::Json)
|
|
21
|
+
end
|
|
22
|
+
else
|
|
23
|
+
load_plugin_class!(new_plugin)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
nil
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
PLUGIN_INTERFACE = [:id, :detect_type, :compatible?, :diff, :stringify].freeze
|
|
31
|
+
|
|
32
|
+
# Load a single plugin class, does not support symbols for builtin plugins.
|
|
33
|
+
def self.load_plugin_class!(plugin)
|
|
34
|
+
plugin_interface = PLUGIN_INTERFACE
|
|
35
|
+
|
|
36
|
+
if plugin.respond_to?(:id)
|
|
37
|
+
if BUILTIN_TYPES.include?(plugin.id)
|
|
38
|
+
plugin_interface = PLUGIN_INTERFACE - [:detect_type]
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
if plugin.id == :unknown
|
|
42
|
+
raise <<~MSG
|
|
43
|
+
plugin #{plugin.inspect} defined #id to = :unknown, but this is not \
|
|
44
|
+
allowed because it would undermine the utility of the #empty? method \
|
|
45
|
+
on the diff.
|
|
46
|
+
MSG
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
missing = plugin_interface.filter do |method_name|
|
|
51
|
+
!plugin.respond_to?(method_name)
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
if missing.any?
|
|
55
|
+
raise <<~MSG
|
|
56
|
+
plugin #{plugin.inspect} does not respond to required methods:
|
|
57
|
+
these are required: #{plugin_interface}
|
|
58
|
+
these were missing: #{missing.inspect}
|
|
59
|
+
MSG
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
::Specdiff.plugins << plugin
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# private
|
|
66
|
+
def self._clear_plugins!
|
|
67
|
+
threadlocal[THREADLOCAL_PLUGINS_KEY] = []
|
|
68
|
+
end
|
|
69
|
+
_clear_plugins!
|
|
70
|
+
|
|
71
|
+
module Plugins
|
|
72
|
+
end
|
|
73
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require "hashdiff"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module WebMock
|
|
5
|
+
class RequestBodyDiff
|
|
6
|
+
def initialize(request_signature, request_stub)
|
|
7
|
+
@request_signature = request_signature
|
|
8
|
+
@request_stub = request_stub
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
PrettyPrintableThingy = Struct.new(:specdiff) do
|
|
12
|
+
# webmock does not print the diff if it responds true to this.
|
|
13
|
+
def empty?
|
|
14
|
+
specdiff.empty?
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# webmock prints the diff by passing us to PP.pp, which in turn uses this
|
|
18
|
+
# method.
|
|
19
|
+
def pretty_print(pp)
|
|
20
|
+
pp.text("\r") # remove a space that isn't supposed to be there
|
|
21
|
+
pp.text(specdiff.to_s)
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def body_diff
|
|
26
|
+
specdiff = Specdiff.diff(request_stub_body, request_signature.body)
|
|
27
|
+
PrettyPrintableThingy.new(specdiff)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
attr_reader :request_signature, :request_stub
|
|
31
|
+
private :request_signature, :request_stub
|
|
32
|
+
|
|
33
|
+
private
|
|
34
|
+
|
|
35
|
+
def request_stub_body
|
|
36
|
+
request_stub.request_pattern &&
|
|
37
|
+
request_stub.request_pattern.body_pattern &&
|
|
38
|
+
request_stub.request_pattern.body_pattern.pattern
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require_relative "webmock/request_body_diff"
|
data/lib/specdiff.rb
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require_relative "specdiff/version"
|
|
2
|
+
require_relative "specdiff/threadlocal"
|
|
3
|
+
require_relative "specdiff/config"
|
|
4
|
+
require_relative "specdiff/colorize"
|
|
5
|
+
|
|
6
|
+
module Specdiff
|
|
7
|
+
# Diff two things
|
|
8
|
+
def self.diff(...)
|
|
9
|
+
::Specdiff::Differ.call(...)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
require_relative "specdiff/diff"
|
|
14
|
+
require_relative "specdiff/differ"
|
|
15
|
+
require_relative "specdiff/plugin"
|
|
16
|
+
require_relative "specdiff/plugins"
|
data/specdiff.gemspec
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "lib/specdiff/version"
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |spec|
|
|
6
|
+
spec.name = "specdiff"
|
|
7
|
+
spec.version = Specdiff::VERSION
|
|
8
|
+
spec.authors = ["Odin Heggvold Bekkelund"]
|
|
9
|
+
spec.email = ["odinhb@protonmail.com"]
|
|
10
|
+
|
|
11
|
+
spec.summary = "Improved request body diffs for webmock"
|
|
12
|
+
spec.homepage = "https://github.com/odinhb/specdiff"
|
|
13
|
+
spec.license = "MIT"
|
|
14
|
+
spec.required_ruby_version = ">= 3.0.0"
|
|
15
|
+
|
|
16
|
+
spec.metadata["homepage_uri"] = spec.homepage
|
|
17
|
+
spec.metadata["source_code_uri"] = spec.homepage
|
|
18
|
+
spec.metadata["changelog_uri"] = "#{spec.homepage}/CHANGELOG.md"
|
|
19
|
+
|
|
20
|
+
# Specify which files should be added to the gem when it is released.
|
|
21
|
+
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
|
22
|
+
spec.files = Dir.chdir(File.expand_path(__dir__)) do
|
|
23
|
+
`git ls-files -z`.split("\x0").reject do |f|
|
|
24
|
+
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
spec.require_paths = ["lib"]
|
|
28
|
+
|
|
29
|
+
spec.add_dependency "hashdiff", "~> 1.0"
|
|
30
|
+
spec.add_dependency "diff-lcs", "~> 1.5"
|
|
31
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: specdiff
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Odin Heggvold Bekkelund
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2023-11-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: hashdiff
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - "~>"
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - "~>"
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: diff-lcs
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - "~>"
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '1.5'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - "~>"
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '1.5'
|
|
41
|
+
description:
|
|
42
|
+
email:
|
|
43
|
+
- odinhb@protonmail.com
|
|
44
|
+
executables: []
|
|
45
|
+
extensions: []
|
|
46
|
+
extra_rdoc_files: []
|
|
47
|
+
files:
|
|
48
|
+
- ".rspec"
|
|
49
|
+
- ".rubocop.yml"
|
|
50
|
+
- ".tool-versions"
|
|
51
|
+
- CHANGELOG.md
|
|
52
|
+
- Gemfile
|
|
53
|
+
- Gemfile.lock
|
|
54
|
+
- LICENSE.txt
|
|
55
|
+
- README.md
|
|
56
|
+
- Rakefile
|
|
57
|
+
- examples/webmock/Gemfile
|
|
58
|
+
- examples/webmock/Gemfile.lock
|
|
59
|
+
- examples/webmock/json.rb
|
|
60
|
+
- examples/webmock/text.rb
|
|
61
|
+
- glossary.txt
|
|
62
|
+
- lib/specdiff.rb
|
|
63
|
+
- lib/specdiff/colorize.rb
|
|
64
|
+
- lib/specdiff/config.rb
|
|
65
|
+
- lib/specdiff/diff.rb
|
|
66
|
+
- lib/specdiff/differ.rb
|
|
67
|
+
- lib/specdiff/differ/hashdiff.rb
|
|
68
|
+
- lib/specdiff/differ/not_found.rb
|
|
69
|
+
- lib/specdiff/differ/text.rb
|
|
70
|
+
- lib/specdiff/plugin.rb
|
|
71
|
+
- lib/specdiff/plugins.rb
|
|
72
|
+
- lib/specdiff/plugins/json.rb
|
|
73
|
+
- lib/specdiff/threadlocal.rb
|
|
74
|
+
- lib/specdiff/version.rb
|
|
75
|
+
- lib/specdiff/webmock.rb
|
|
76
|
+
- lib/specdiff/webmock/request_body_diff.rb
|
|
77
|
+
- specdiff.gemspec
|
|
78
|
+
homepage: https://github.com/odinhb/specdiff
|
|
79
|
+
licenses:
|
|
80
|
+
- MIT
|
|
81
|
+
metadata:
|
|
82
|
+
homepage_uri: https://github.com/odinhb/specdiff
|
|
83
|
+
source_code_uri: https://github.com/odinhb/specdiff
|
|
84
|
+
changelog_uri: https://github.com/odinhb/specdiff/CHANGELOG.md
|
|
85
|
+
post_install_message:
|
|
86
|
+
rdoc_options: []
|
|
87
|
+
require_paths:
|
|
88
|
+
- lib
|
|
89
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
90
|
+
requirements:
|
|
91
|
+
- - ">="
|
|
92
|
+
- !ruby/object:Gem::Version
|
|
93
|
+
version: 3.0.0
|
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
95
|
+
requirements:
|
|
96
|
+
- - ">="
|
|
97
|
+
- !ruby/object:Gem::Version
|
|
98
|
+
version: '0'
|
|
99
|
+
requirements: []
|
|
100
|
+
rubygems_version: 3.4.10
|
|
101
|
+
signing_key:
|
|
102
|
+
specification_version: 4
|
|
103
|
+
summary: Improved request body diffs for webmock
|
|
104
|
+
test_files: []
|