suture 1.1.1 → 1.1.2
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 +5 -5
- data/.gitignore +1 -0
- data/.standard.yml +7 -0
- data/.travis.yml +6 -4
- data/CHANGELOG.md +11 -0
- data/Gemfile +1 -1
- data/README.md +36 -23
- data/Rakefile +10 -11
- data/lib/suture.rb +0 -1
- data/lib/suture/adapter/dictaphone.rb +1 -2
- data/lib/suture/comparator.rb +5 -5
- data/lib/suture/config.rb +1 -1
- data/lib/suture/create/builds_plan.rb +1 -1
- data/lib/suture/create/chooses_surgeon.rb +1 -1
- data/lib/suture/create/validates_plan.rb +15 -15
- data/lib/suture/error/invalid_plan.rb +5 -5
- data/lib/suture/error/observation_conflict.rb +1 -1
- data/lib/suture/error/result_mismatch.rb +1 -1
- data/lib/suture/error/verification_failed.rb +14 -16
- data/lib/suture/reset.rb +0 -1
- data/lib/suture/surgeon/auditor.rb +5 -7
- data/lib/suture/surgeon/no_op.rb +1 -1
- data/lib/suture/surgeon/observer.rb +2 -3
- data/lib/suture/surgeon/remediator.rb +6 -8
- data/lib/suture/util/compares_results.rb +1 -1
- data/lib/suture/util/env.rb +4 -4
- data/lib/suture/util/scalpel.rb +5 -6
- data/lib/suture/util/shuffle.rb +1 -1
- data/lib/suture/value/observation.rb +1 -1
- data/lib/suture/value/plan.rb +4 -4
- data/lib/suture/value/result.rb +2 -2
- data/lib/suture/value/test_plan.rb +12 -12
- data/lib/suture/value/test_results.rb +0 -1
- data/lib/suture/verify.rb +0 -1
- data/lib/suture/verify/administers_test.rb +4 -4
- data/lib/suture/verify/interprets_results.rb +0 -2
- data/lib/suture/verify/prescribes_test_plan.rb +1 -1
- data/lib/suture/verify/tests_patient.rb +4 -5
- data/lib/suture/version.rb +1 -1
- data/lib/suture/wrap/logger.rb +35 -15
- data/lib/suture/wrap/sqlite.rb +1 -1
- data/suture.gemspec +10 -5
- metadata +18 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 163a4b24e5b0e5a27e41455d0fab014c19e96d9e7abe9b5ebf35d984094fdb95
|
4
|
+
data.tar.gz: 6f5f28071fd9f3cb298fd16f7fe5ddf7b66e57941792b6e0142daeffb2965677
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8d2bef6ec660786f4b3da2722c0e95b722e7f456ce4347da9e6e36627eff99446c5280d708a2c2b639d1543dfd1ddaa0ebb7c4a0cf7a101e9295eaef49063c7
|
7
|
+
data.tar.gz: dd00faab3aca2bff8fe346170d0a31d2e80ecaf0a08ee7b2794900f25ef7a48b4db542b8a0c38e4fda21ad3c052d5638d555560a971d8a48149617a38349b172
|
data/.gitignore
CHANGED
data/.standard.yml
ADDED
data/.travis.yml
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
language: ruby
|
2
2
|
sudo: false
|
3
|
-
before_install:
|
4
|
-
- gem install bundler
|
5
|
-
- cd example/rails_app && ./script/setup.sh && cd ../..
|
6
3
|
rvm:
|
7
4
|
- 1.8.7
|
8
5
|
- 1.9
|
9
6
|
- 2.0
|
10
7
|
- 2.1
|
11
8
|
- 2.2
|
12
|
-
- 2.3.
|
9
|
+
- 2.3.3
|
10
|
+
before_install:
|
11
|
+
- gem install bundler
|
12
|
+
- cd example/rails_app && ./script/setup.sh && cd ../..
|
13
|
+
after_success:
|
14
|
+
- bundle exec codeclimate-test-reporter
|
13
15
|
addons:
|
14
16
|
code_climate:
|
15
17
|
repo_token: 05e3e31164d59aa626b730b92eb9b7418326dbf23420a4b87eab2555840b39ef
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
# Change Log
|
2
2
|
|
3
|
+
## [v1.1.1](https://github.com/testdouble/suture/tree/v1.1.1) (2016-09-19)
|
4
|
+
[Full Changelog](https://github.com/testdouble/suture/compare/v1.1.0...v1.1.1)
|
5
|
+
|
6
|
+
**Closed issues:**
|
7
|
+
|
8
|
+
- call\_both: if raise\_on\_result\_mismatch is false and return\_old\_on\_result\_mismatch is true, rescue errors from :new path [\#64](https://github.com/testdouble/suture/issues/64)
|
9
|
+
|
10
|
+
**Merged pull requests:**
|
11
|
+
|
12
|
+
- Allow graceful fallback by call\_both when new code path raises [\#65](https://github.com/testdouble/suture/pull/65) ([searls](https://github.com/searls))
|
13
|
+
|
3
14
|
## [v1.1.0](https://github.com/testdouble/suture/tree/v1.1.0) (2016-09-18)
|
4
15
|
[Full Changelog](https://github.com/testdouble/suture/compare/v1.0.0...v1.1.0)
|
5
16
|
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -9,11 +9,21 @@ Suture hopes to make it safer to completely reimplement a code path.
|
|
9
9
|
Suture provides help to the entire lifecycle of refactoring poorly-understood
|
10
10
|
code, from local development, to a staging environment, and even in production.
|
11
11
|
|
12
|
+
# Video
|
13
|
+
|
14
|
+
Suture was unveiled at Ruby Kaigi 2016 as a one approach that we can make
|
15
|
+
refactors less scary and more predictable. You can watch the 45 minute screencast
|
16
|
+
here:
|
17
|
+
|
18
|
+
[<img width="803" alt="screen shot 2016-09-20 at 9 44 43 am" src="https://cloud.githubusercontent.com/assets/79303/18653743/e669d304-7f16-11e6-84e7-c86b8f88132c.png" alt="A presentation on Suture">](http://blog.testdouble.com/posts/2016-09-16-surgical-refactors-with-suture.html)
|
19
|
+
|
20
|
+
# Walk-through guide
|
21
|
+
|
12
22
|
Refactoring or reimplementing important code is an involved process! Instead of
|
13
23
|
listing out Suture's API without sufficient exposition, here is an example that
|
14
24
|
we'll take you through each stage of the lifecycle.
|
15
25
|
|
16
|
-
##
|
26
|
+
## Development
|
17
27
|
|
18
28
|
Suppose you have a really nasty worker method:
|
19
29
|
|
@@ -58,9 +68,12 @@ class LegacyWorker
|
|
58
68
|
end
|
59
69
|
```
|
60
70
|
|
61
|
-
As you can see, the call to `MyMailer.send` is left at the original call site
|
62
|
-
|
63
|
-
|
71
|
+
As you can see, the call to `MyMailer.send` is left at the original call site.
|
72
|
+
`MyMailer.send` is effectively a void method being invoked for its side effect,
|
73
|
+
which would make it difficult to test. By creating `LegacyWorker#call`, we can
|
74
|
+
now express the work more clearly in terms of repeatable inputs (`id`) and
|
75
|
+
outputs (`thing.result`), which will help us verify that our refactor is working
|
76
|
+
later.
|
64
77
|
|
65
78
|
Since any changes to the code while it's untested are very dangerous, it's
|
66
79
|
important to minimize changes made for the sake of creating a clear seam.
|
@@ -89,10 +102,10 @@ LegacyWorker without taking any other meaningful action.
|
|
89
102
|
|
90
103
|
### 3. Record the current behavior
|
91
104
|
|
92
|
-
Next, we want to start observing how the legacy worker is actually called
|
93
|
-
arguments are sent to it and what
|
94
|
-
as we use our app locally, we can later
|
95
|
-
implementations behave the same way.
|
105
|
+
Next, we want to start observing how the legacy worker is actually called. What
|
106
|
+
arguments are being sent to it and what value does it returns (or, what error
|
107
|
+
does it raise)? By recording the calls as we use our app locally, we can later
|
108
|
+
test that the old and new implementations behave the same way.
|
96
109
|
|
97
110
|
First, we tell Suture to start recording calls by setting the environment
|
98
111
|
variable `SUTURE_RECORD_CALLS` to something truthy (e.g.
|
@@ -101,8 +114,8 @@ any calls to our seam will record the arguments passed to the legacy code path
|
|
101
114
|
and the return value.
|
102
115
|
|
103
116
|
As you use the application (whether it's a queue system, a web app, or a CLI),
|
104
|
-
the calls will be saved to a sqlite database.
|
105
|
-
external data sources or services,
|
117
|
+
the calls will be saved to a sqlite database. Keep in mind that if the legacy code
|
118
|
+
path relies on external data sources or services, your recorded inputs and
|
106
119
|
outputs will rely on them as well. You may want to narrow the scope of your
|
107
120
|
seam accordingly (e.g. to receive an object as an argument instead of a database
|
108
121
|
id).
|
@@ -146,7 +159,7 @@ expected value. It's a good idea to run this against the legacy code first,
|
|
146
159
|
for two reasons:
|
147
160
|
|
148
161
|
* running the characterization tests against the legacy code path will ensure
|
149
|
-
the test environment has the data needed to behave the same way as when it was
|
162
|
+
that the test environment has the data needed to behave the same way as when it was
|
150
163
|
recorded (it may be appropriate to take a snapshot of the database before you
|
151
164
|
start recording and load it before you run your tests)
|
152
165
|
|
@@ -158,13 +171,13 @@ start recording and load it before you run your tests)
|
|
158
171
|
subordinate to it) to make sure our characterization test sufficiently
|
159
172
|
exercises it
|
160
173
|
* identify incidental coverage of code paths that are outside the scope of
|
161
|
-
what we hope to refactor
|
174
|
+
what we hope to refactor. This will help to see if `LegacyWorker` has
|
162
175
|
side effects we didn't anticipate and should additionally write tests for
|
163
176
|
|
164
177
|
### 5. Specify and test a path for new code
|
165
178
|
|
166
|
-
Once
|
167
|
-
can start work on a `NewWorker`. To get started, we
|
179
|
+
Once the automated characterization test of our recordings is passing, then we
|
180
|
+
can start work on a `NewWorker`. To get started, we update our Suture
|
168
181
|
configuration:
|
169
182
|
|
170
183
|
``` ruby
|
@@ -214,7 +227,7 @@ end
|
|
214
227
|
```
|
215
228
|
|
216
229
|
Obviously, this should fail until `NewWorker`'s implementation covers all the
|
217
|
-
cases we recorded from `LegacyWorker`.
|
230
|
+
cases that we recorded from `LegacyWorker`.
|
218
231
|
|
219
232
|
Remember, characterization tests aren't designed to be kept around forever. Once
|
220
233
|
you're confident that the new implementation is sufficient, it's typically better
|
@@ -225,21 +238,21 @@ implementation and its component parts.
|
|
225
238
|
|
226
239
|
This step is the hardest part and there's not much Suture can do to make it
|
227
240
|
any easier. How you go about implementing your improvements depends on whether
|
228
|
-
you intend to rewrite the legacy code path or refactor it. Some
|
229
|
-
approach
|
241
|
+
you intend to rewrite the legacy code path or refactor it. Some comments on each
|
242
|
+
approach follow:
|
230
243
|
|
231
244
|
#### Reimplementing
|
232
245
|
|
233
246
|
The best time to rewrite a piece of software is when you have a better
|
234
|
-
understanding of the real-world process it models than the original authors did
|
247
|
+
understanding of the real-world process that it models than the original authors did
|
235
248
|
when they first wrote it. If that's the case, it's likely you'll think of more
|
236
249
|
reliable names and abstractions than they did.
|
237
250
|
|
238
251
|
As for workflow, consider writing the new implementation like you would any other
|
239
|
-
new part of the system
|
252
|
+
new part of the system. The added benefit is being able to run the
|
240
253
|
characterization tests as a progress indicator and a backstop for any missed edge
|
241
254
|
cases. The ultimate goal of this workflow should be to incrementally arrive at a
|
242
|
-
clean design that completely passes the characterization test run by
|
255
|
+
clean design that completely passes the characterization test run by running
|
243
256
|
`Suture.verify`.
|
244
257
|
|
245
258
|
#### Refactoring
|
@@ -323,7 +336,7 @@ Suture.config({
|
|
323
336
|
```
|
324
337
|
|
325
338
|
When your new code path raises an error with the above settings, it will
|
326
|
-
|
339
|
+
propagate and log the error to the specified file.
|
327
340
|
|
328
341
|
### Custom error handlers
|
329
342
|
|
@@ -478,7 +491,7 @@ certain cases, setting `:expected_error_types => [WidgetError]` will result in:
|
|
478
491
|
`kind_of?` any recorded error type (regardless of whether `Suture.verify` is
|
479
492
|
passed a redundant list of `expected_error_types`)
|
480
493
|
* `Suture.create`, when `fallback_on_error` is enabled, will allow expected
|
481
|
-
errors raised by the `new` path to
|
494
|
+
errors raised by the `new` path to propagate, as opposed to logging &
|
482
495
|
rescuing them before calling the `old` path as a fallback
|
483
496
|
|
484
497
|
* _disable_ - (Default: `false`) - when enabled, Suture will attempt to revert to
|
@@ -641,7 +654,7 @@ detects two ActiveRecord model instances being compared, it will behave
|
|
641
654
|
differently, by this logic:
|
642
655
|
|
643
656
|
1. Instead of comparing the objects with `==` (which returns true so long as the
|
644
|
-
`id` attribute
|
657
|
+
`id` attribute matches), Suture will compare the objects' `attributes` hashes
|
645
658
|
instead
|
646
659
|
2. The built-in `updated_at` and `created_at` will typically differ when code
|
647
660
|
is executed at different times and are usually not meaningful to application
|
data/Rakefile
CHANGED
@@ -4,13 +4,13 @@ require "rake/testtask"
|
|
4
4
|
Rake::TestTask.new(:unit) do |t|
|
5
5
|
t.libs << "test"
|
6
6
|
t.libs << "lib"
|
7
|
-
t.test_files = FileList[
|
7
|
+
t.test_files = FileList["test/helper.rb", "test/**/*_test.rb"]
|
8
8
|
end
|
9
9
|
|
10
10
|
Rake::TestTask.new(:safe) do |t|
|
11
11
|
t.libs << "safe"
|
12
12
|
t.libs << "lib"
|
13
|
-
t.test_files = FileList[
|
13
|
+
t.test_files = FileList["safe/helper.rb", "safe/**/*_test.rb"]
|
14
14
|
end
|
15
15
|
|
16
16
|
Rake::TestTask.new(:test) do |t|
|
@@ -18,11 +18,11 @@ Rake::TestTask.new(:test) do |t|
|
|
18
18
|
t.libs << "safe"
|
19
19
|
t.libs << "lib"
|
20
20
|
t.test_files = FileList[
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
21
|
+
"safe/support/code_climate",
|
22
|
+
"test/helper.rb",
|
23
|
+
"test/**/*_test.rb",
|
24
|
+
"safe/helper.rb",
|
25
|
+
"safe/**/*_test.rb"
|
26
26
|
]
|
27
27
|
end
|
28
28
|
|
@@ -32,14 +32,14 @@ task :example do
|
|
32
32
|
BUNDLE_GEMFILE="$PWD/Gemfile" bundle install --quiet
|
33
33
|
BUNDLE_GEMFILE="$PWD/Gemfile" bundle exec rake suture
|
34
34
|
SH
|
35
|
-
|
36
|
-
raise StandardError
|
35
|
+
unless passed
|
36
|
+
raise StandardError, "Rails example failed!"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
if Gem.ruby_version >= Gem::Version.new("2.2.2")
|
42
|
-
require
|
42
|
+
require "github_changelog_generator/task"
|
43
43
|
GitHubChangelogGenerator::RakeTask.new :changelog
|
44
44
|
task :changelog_commit do
|
45
45
|
require "suture"
|
@@ -50,5 +50,4 @@ if Gem.ruby_version >= Gem::Version.new("2.2.2")
|
|
50
50
|
Rake::Task["release:rubygem_push"].enhance([:changelog, :changelog_commit])
|
51
51
|
end
|
52
52
|
|
53
|
-
|
54
53
|
task :default => [:test, :example]
|
data/lib/suture.rb
CHANGED
@@ -19,7 +19,6 @@ module Suture::Adapter
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
|
23
22
|
def record(returned_value)
|
24
23
|
record_result(Suture::Value::Result.returned(returned_value))
|
25
24
|
end
|
@@ -48,7 +47,7 @@ module Suture::Adapter
|
|
48
47
|
Suture::Wrap::Sqlite.delete(@db, :observations, "where name = ?", [name.to_s])
|
49
48
|
end
|
50
49
|
|
51
|
-
|
50
|
+
private
|
52
51
|
|
53
52
|
def record_result(result)
|
54
53
|
Suture::Wrap::Sqlite.insert(
|
data/lib/suture/comparator.rb
CHANGED
@@ -7,7 +7,7 @@ module Suture
|
|
7
7
|
:active_record_excluded_attributes => (
|
8
8
|
options[:active_record_excluded_attributes] ||
|
9
9
|
DEFAULT_ACTIVE_RECORD_EXCLUDED_ATTRIBUTES
|
10
|
-
).map(&:to_s)
|
10
|
+
).map(&:to_s),
|
11
11
|
}
|
12
12
|
end
|
13
13
|
|
@@ -23,7 +23,7 @@ module Suture
|
|
23
23
|
protected
|
24
24
|
|
25
25
|
def compare_active_record(recorded, actual)
|
26
|
-
actual.
|
26
|
+
actual.is_a?(recorded.class) &&
|
27
27
|
without_excluded_attrs(recorded.attributes) ==
|
28
28
|
without_excluded_attrs(actual.attributes)
|
29
29
|
end
|
@@ -31,7 +31,7 @@ module Suture
|
|
31
31
|
private
|
32
32
|
|
33
33
|
def without_excluded_attrs(hash)
|
34
|
-
hash.reject do |k,
|
34
|
+
hash.reject do |k, _v|
|
35
35
|
@options[:active_record_excluded_attributes].include?(k.to_s)
|
36
36
|
end
|
37
37
|
end
|
@@ -46,8 +46,8 @@ module Suture
|
|
46
46
|
|
47
47
|
def is_active_record?(recorded, actual)
|
48
48
|
defined?(ActiveRecord::Base) &&
|
49
|
-
recorded.
|
50
|
-
actual.
|
49
|
+
recorded.is_a?(ActiveRecord::Base) &&
|
50
|
+
actual.is_a?(ActiveRecord::Base)
|
51
51
|
end
|
52
52
|
end
|
53
53
|
end
|
data/lib/suture/config.rb
CHANGED
@@ -12,7 +12,7 @@ module Suture
|
|
12
12
|
return Surgeon::NoOp.new if plan.disable
|
13
13
|
if plan.record_calls
|
14
14
|
if plan.new
|
15
|
-
log_warn <<-MSG.gsub(/^ {12}/,
|
15
|
+
log_warn <<-MSG.gsub(/^ {12}/, "")
|
16
16
|
Seam #{plan.name.inspect} has a :new code path defined, but because
|
17
17
|
it is set to :record_calls, we will invoke the :old code path
|
18
18
|
instead. If this is not what you intend, set :record_calls to false.
|
@@ -5,28 +5,28 @@ module Suture
|
|
5
5
|
REQUIREMENTS = {
|
6
6
|
:name => "in order to identify recorded calls",
|
7
7
|
:old => "in order to call the legacy code path (must respond to `:call`)",
|
8
|
-
:args => "in order to differentiate recorded calls (if the code you're changing doesn't take arguments, you can set :args to `[]` but should probably consider creating a seam inside of it which can--consult the README for more advice)"
|
8
|
+
:args => "in order to differentiate recorded calls (if the code you're changing doesn't take arguments, you can set :args to `[]` but should probably consider creating a seam inside of it which can--consult the README for more advice)",
|
9
9
|
}
|
10
10
|
|
11
11
|
VALIDATIONS = {
|
12
12
|
:name => {
|
13
13
|
:test => lambda { |name| name.to_s.size < 256 },
|
14
|
-
:message => "must be less than 256 characters"
|
14
|
+
:message => "must be less than 256 characters",
|
15
15
|
},
|
16
|
-
:old => CALLABLE_VALIDATION =
|
16
|
+
:old => CALLABLE_VALIDATION = {
|
17
17
|
:test => lambda { |old| old.respond_to?(:call) },
|
18
|
-
:message => "must respond to `call` (e.g. `dog.method(:bark)` or `->(*args){ dog.bark(*args) }`)"
|
19
|
-
}
|
18
|
+
:message => "must respond to `call` (e.g. `dog.method(:bark)` or `->(*args){ dog.bark(*args) }`)",
|
19
|
+
},
|
20
20
|
:new => CALLABLE_VALIDATION,
|
21
21
|
:comparator => CALLABLE_VALIDATION.merge(
|
22
22
|
:message => "must respond to `call` (e.g. `MyComparator.new` or `->(recorded, actual) { recorded == actual }`)"
|
23
|
-
)
|
23
|
+
),
|
24
24
|
}
|
25
25
|
|
26
26
|
CONFLICTS = [
|
27
27
|
lambda { |plan|
|
28
28
|
if plan.record_calls && !plan.database_path
|
29
|
-
<<-MSG.gsub(/^ {12}/,
|
29
|
+
<<-MSG.gsub(/^ {12}/, "")
|
30
30
|
:record_calls is enabled, but :database_path is nil, so Suture
|
31
31
|
doesn't know where to record calls to the seam.
|
32
32
|
MSG
|
@@ -34,7 +34,7 @@ module Suture
|
|
34
34
|
},
|
35
35
|
lambda { |plan|
|
36
36
|
if plan.record_calls && plan.call_both
|
37
|
-
<<-MSG.gsub(/^ {12}/,
|
37
|
+
<<-MSG.gsub(/^ {12}/, "")
|
38
38
|
:record_calls & :call_both are both enabled and conflict with one
|
39
39
|
another. :record_calls will only invoke the old code path (intended
|
40
40
|
for characterization of the old code path and initial development
|
@@ -51,7 +51,7 @@ module Suture
|
|
51
51
|
},
|
52
52
|
lambda { |plan|
|
53
53
|
if plan.record_calls && plan.fallback_on_error
|
54
|
-
<<-MSG.gsub(/^ {12}/,
|
54
|
+
<<-MSG.gsub(/^ {12}/, "")
|
55
55
|
:record_calls & :fallback_on_error are both enabled and conflict with
|
56
56
|
one another. :record_calls will only invoke the old code path
|
57
57
|
(intended for characterization of the old code path and initial
|
@@ -63,7 +63,7 @@ module Suture
|
|
63
63
|
},
|
64
64
|
lambda { |plan|
|
65
65
|
if plan.call_both && plan.fallback_on_error
|
66
|
-
<<-MSG.gsub(/^ {12}/,
|
66
|
+
<<-MSG.gsub(/^ {12}/, "")
|
67
67
|
:call_both & :fallback_on_error are both enabled and conflict with
|
68
68
|
one another. :call_both is designed for pre-production environments
|
69
69
|
and will call both the old and new code paths to compare their
|
@@ -75,7 +75,7 @@ module Suture
|
|
75
75
|
},
|
76
76
|
lambda { |plan|
|
77
77
|
if plan.call_both && !plan.new.respond_to?(:call)
|
78
|
-
<<-MSG.gsub(/^ {12}/,
|
78
|
+
<<-MSG.gsub(/^ {12}/, "")
|
79
79
|
:call_both is set but :new is either not set or is not callable. In
|
80
80
|
order to call both code paths, both :old and :new must be set and
|
81
81
|
callable
|
@@ -84,7 +84,7 @@ module Suture
|
|
84
84
|
},
|
85
85
|
lambda { |plan|
|
86
86
|
if plan.fallback_on_error && !plan.new.respond_to?(:call)
|
87
|
-
<<-MSG.gsub(/^ {12}/,
|
87
|
+
<<-MSG.gsub(/^ {12}/, "")
|
88
88
|
:fallback_on_error is set but :new is either not set or is not
|
89
89
|
callable. This mode is designed for after the :new code path has
|
90
90
|
been developed and run in production-like environments, where :old
|
@@ -96,13 +96,13 @@ module Suture
|
|
96
96
|
},
|
97
97
|
lambda { |plan|
|
98
98
|
if !plan.raise_on_result_mismatch && !plan.call_both
|
99
|
-
<<-MSG.gsub(/^ {12}/,
|
99
|
+
<<-MSG.gsub(/^ {12}/, "")
|
100
100
|
:raise_on_result_mismatch was disabled but :call_both is not enabled.
|
101
101
|
This option only applies to the :call_both mode, and will have no
|
102
102
|
impact when set for other modes
|
103
103
|
MSG
|
104
104
|
end
|
105
|
-
}
|
105
|
+
},
|
106
106
|
]
|
107
107
|
|
108
108
|
def validate(plan)
|
@@ -125,7 +125,7 @@ module Suture
|
|
125
125
|
|
126
126
|
def invalid_attrs(plan)
|
127
127
|
VALIDATIONS.select { |name, rule|
|
128
|
-
next unless attr = plan.send(name)
|
128
|
+
next unless (attr = plan.send(name))
|
129
129
|
!rule[:test].call(attr)
|
130
130
|
}
|
131
131
|
end
|
@@ -3,31 +3,31 @@ module Suture::Error
|
|
3
3
|
HEADER = "Suture was unable to create your seam, because options passed to `Suture.create` were invalid."
|
4
4
|
|
5
5
|
def self.missing_requirements(requirements)
|
6
|
-
new <<-MSG.gsub(/^ {8}/,
|
6
|
+
new <<-MSG.gsub(/^ {8}/, "")
|
7
7
|
#{HEADER}
|
8
8
|
|
9
9
|
The following options are required:
|
10
10
|
|
11
|
-
#{requirements.map {|(name,explanation)|
|
11
|
+
#{requirements.map {|(name, explanation)|
|
12
12
|
"* #{name.inspect} - #{explanation}"
|
13
13
|
}.join("\n ")}
|
14
14
|
MSG
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.invalid_options(invalids)
|
18
|
-
new <<-MSG.gsub(/^ {8}/,
|
18
|
+
new <<-MSG.gsub(/^ {8}/, "")
|
19
19
|
#{HEADER}
|
20
20
|
|
21
21
|
The following options were invalid:
|
22
22
|
|
23
|
-
#{invalids.map {|(name,rule)|
|
23
|
+
#{invalids.map {|(name, rule)|
|
24
24
|
"* #{name.inspect} - #{rule[:message]}"
|
25
25
|
}.join("\n ")}
|
26
26
|
MSG
|
27
27
|
end
|
28
28
|
|
29
29
|
def self.conflicting_options(conflicts)
|
30
|
-
new <<-MSG.gsub(/^ {8}/,
|
30
|
+
new <<-MSG.gsub(/^ {8}/, "")
|
31
31
|
#{HEADER}
|
32
32
|
|
33
33
|
Suture isn't sure how to best handle the combination of options passed:
|
@@ -7,7 +7,7 @@ module Suture::Error
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def message
|
10
|
-
<<-MSG.gsub(/^ {8}/,
|
10
|
+
<<-MSG.gsub(/^ {8}/, "")
|
11
11
|
The results from the old & new code paths did not match for the seam
|
12
12
|
#{@plan.name.inspect} and Suture is raising this error because the `:call_both`
|
13
13
|
option is enabled, because both code paths are expected to return the
|
@@ -14,7 +14,7 @@ module Suture::Error
|
|
14
14
|
describe_failures(@results.failed, @plan),
|
15
15
|
configuration(@plan),
|
16
16
|
summarize(@results),
|
17
|
-
progress(@plan, @results)
|
17
|
+
progress(@plan, @results),
|
18
18
|
].compact.join("\n").tap do
|
19
19
|
end
|
20
20
|
end
|
@@ -22,7 +22,7 @@ module Suture::Error
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def intro
|
25
|
-
<<-MSG.gsub(/^ {8}/,
|
25
|
+
<<-MSG.gsub(/^ {8}/, "")
|
26
26
|
|
27
27
|
# Verification of your seam failed!
|
28
28
|
|
@@ -31,7 +31,7 @@ module Suture::Error
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def summarize(results)
|
34
|
-
<<-MSG.gsub(/^ {8}/,
|
34
|
+
<<-MSG.gsub(/^ {8}/, "")
|
35
35
|
# Result Summary
|
36
36
|
|
37
37
|
- Passed........#{results.passed_count}
|
@@ -49,7 +49,7 @@ module Suture::Error
|
|
49
49
|
results.all.size
|
50
50
|
)
|
51
51
|
percent = Suture::Util::Numbers.percent(results.passed.size, results.all.size)
|
52
|
-
<<-MSG.gsub(/^ {8}/,
|
52
|
+
<<-MSG.gsub(/^ {8}/, "")
|
53
53
|
## Progress
|
54
54
|
|
55
55
|
Here's what your progress to initial completion looks like so far:
|
@@ -61,7 +61,7 @@ module Suture::Error
|
|
61
61
|
end
|
62
62
|
|
63
63
|
def configuration(plan)
|
64
|
-
<<-MSG.gsub(/^ {8}/,
|
64
|
+
<<-MSG.gsub(/^ {8}/, "")
|
65
65
|
# Configuration
|
66
66
|
|
67
67
|
This is the configuration used by this test run:
|
@@ -81,7 +81,7 @@ module Suture::Error
|
|
81
81
|
end
|
82
82
|
|
83
83
|
def describe_comparator(comparator)
|
84
|
-
if comparator.
|
84
|
+
if comparator.is_a?(Proc)
|
85
85
|
"Proc # (in: `#{describe_source_location(*comparator.source_location)}`)"
|
86
86
|
elsif comparator.respond_to?(:method) && comparator.method(:call)
|
87
87
|
"#{comparator.inspect}.new, # (in: `#{describe_source_location(*comparator.method(:call).source_location)}`)"
|
@@ -90,7 +90,7 @@ module Suture::Error
|
|
90
90
|
|
91
91
|
def describe_source_location(file, line)
|
92
92
|
root = File.join(Dir.getwd, "/")
|
93
|
-
path = file.start_with?(root) ? file.gsub(root,
|
93
|
+
path = file.start_with?(root) ? file.gsub(root, "") : file
|
94
94
|
"#{path}:#{line}"
|
95
95
|
end
|
96
96
|
|
@@ -102,13 +102,13 @@ module Suture::Error
|
|
102
102
|
describe_failure(failure, index) if plan.error_message_limit.nil? || index < plan.error_message_limit
|
103
103
|
},
|
104
104
|
describe_squelched_failure_messages(failures.size, plan.error_message_limit),
|
105
|
-
describe_general_failure_advice(plan)
|
105
|
+
describe_general_failure_advice(plan),
|
106
106
|
].flatten.compact.join("\n")
|
107
107
|
end
|
108
108
|
|
109
109
|
def describe_failure(failure, index)
|
110
110
|
expected = failure[:observation]
|
111
|
-
|
111
|
+
<<-MSG.gsub(/^ {8}/, "")
|
112
112
|
#{index + 1}.) Recorded call for seam #{expected.name.inspect} (ID: #{expected.id}) ran and #{failure[:error] ? "raised an error" : "failed comparison"}.
|
113
113
|
|
114
114
|
Arguments: ```
|
@@ -117,13 +117,12 @@ module Suture::Error
|
|
117
117
|
Expected #{expected.result.errored? ? "error raised" : "returned value"}: ```
|
118
118
|
#{expected.result.value.inspect}
|
119
119
|
```
|
120
|
-
Actual #{
|
120
|
+
Actual #{failure[:error] || failure[:new_result].errored? ? "error raised" : "returned value"}: ```
|
121
121
|
#{if failure[:error]
|
122
122
|
stringify_error(failure[:error])
|
123
123
|
else
|
124
124
|
failure[:new_result].value.inspect
|
125
|
-
end
|
126
|
-
}
|
125
|
+
end}
|
127
126
|
```
|
128
127
|
|
129
128
|
Ideas to fix this:
|
@@ -138,7 +137,7 @@ module Suture::Error
|
|
138
137
|
end
|
139
138
|
|
140
139
|
def describe_general_failure_advice(plan)
|
141
|
-
<<-MSG.gsub(/^ {8}/,
|
140
|
+
<<-MSG.gsub(/^ {8}/, "")
|
142
141
|
### Fixing these failures
|
143
142
|
|
144
143
|
#### Custom comparator
|
@@ -156,7 +155,7 @@ module Suture::Error
|
|
156
155
|
behavior somewhere in your subject's code.
|
157
156
|
|
158
157
|
#{if !plan.random_seed.nil?
|
159
|
-
<<-MOAR.gsub(/^ {14}/,
|
158
|
+
<<-MOAR.gsub(/^ {14}/, "")
|
160
159
|
To re-run the tests with the same random seed as was used in this run,
|
161
160
|
set the env var `SUTURE_RANDOM_SEED=#{plan.random_seed}` or the config entry
|
162
161
|
`:random_seed => #{plan.random_seed}`.
|
@@ -167,7 +166,7 @@ module Suture::Error
|
|
167
166
|
`:random_seed => nil`.
|
168
167
|
MOAR
|
169
168
|
else
|
170
|
-
<<-MOAR.gsub(/^ {14}/,
|
169
|
+
<<-MOAR.gsub(/^ {14}/, "")
|
171
170
|
This test was run in insertion order (by the primary key of the table
|
172
171
|
that stores calls, ascending). This is sometimes necessary when the
|
173
172
|
code has an order-dependent side effect, but shouldn't be set unless it's
|
@@ -180,7 +179,6 @@ module Suture::Error
|
|
180
179
|
MSG
|
181
180
|
end
|
182
181
|
|
183
|
-
|
184
182
|
def stringify_error(error)
|
185
183
|
s = error.inspect
|
186
184
|
s += "\n" + error.backtrace.join("\n") if error.backtrace
|
data/lib/suture/reset.rb
CHANGED
@@ -24,11 +24,9 @@ module Suture::Surgeon
|
|
24
24
|
private
|
25
25
|
|
26
26
|
def result_for(plan, path)
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
Suture::Value::Result.errored(error)
|
31
|
-
end
|
27
|
+
Suture::Value::Result.returned(@scalpel.cut(plan, path))
|
28
|
+
rescue => error
|
29
|
+
Suture::Value::Result.errored(error)
|
32
30
|
end
|
33
31
|
|
34
32
|
def comparable?(plan, old_result, new_result)
|
@@ -53,12 +51,12 @@ module Suture::Surgeon
|
|
53
51
|
if result.errored?
|
54
52
|
raise result.value
|
55
53
|
else
|
56
|
-
|
54
|
+
result.value
|
57
55
|
end
|
58
56
|
end
|
59
57
|
|
60
58
|
def log_warning(plan, old_result, new_result)
|
61
|
-
log_warn <<-MSG.gsub(/^ {8}/,
|
59
|
+
log_warn <<-MSG.gsub(/^ {8}/, "")
|
62
60
|
Seam #{plan.name.inspect} is set to :call_both the :new and :old code
|
63
61
|
paths, but they did not match.
|
64
62
|
|
data/lib/suture/surgeon/no_op.rb
CHANGED
@@ -9,8 +9,8 @@ module Suture::Surgeon
|
|
9
9
|
Suture::Util::Scalpel.new.cut(plan, :old).tap do |result|
|
10
10
|
dictaphone.record(result)
|
11
11
|
end
|
12
|
-
rescue
|
13
|
-
if plan.expected_error_types.any? {|e| error.
|
12
|
+
rescue => error
|
13
|
+
if plan.expected_error_types.any? {|e| error.is_a?(e) }
|
14
14
|
dictaphone.record_error(error)
|
15
15
|
end
|
16
16
|
raise error
|
@@ -18,4 +18,3 @@ module Suture::Surgeon
|
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
21
|
-
|
@@ -7,14 +7,12 @@ module Suture::Surgeon
|
|
7
7
|
end
|
8
8
|
|
9
9
|
def operate(plan)
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
@scalpel.cut(plan, :old)
|
17
|
-
end
|
10
|
+
@scalpel.cut(plan, :new)
|
11
|
+
rescue => actual_error
|
12
|
+
if plan.expected_error_types.any? { |e| actual_error.is_a?(e) }
|
13
|
+
raise actual_error
|
14
|
+
else
|
15
|
+
@scalpel.cut(plan, :old)
|
18
16
|
end
|
19
17
|
end
|
20
18
|
end
|
@@ -8,7 +8,7 @@ module Suture::Util
|
|
8
8
|
if expected.errored? != actual.errored?
|
9
9
|
false
|
10
10
|
elsif expected.errored?
|
11
|
-
actual.value.
|
11
|
+
actual.value.is_a?(expected.value.class) &&
|
12
12
|
expected.value.message == actual.value.message
|
13
13
|
else
|
14
14
|
@comparator.call(expected.value, actual.value)
|
data/lib/suture/util/env.rb
CHANGED
@@ -3,15 +3,15 @@ module Suture::Util
|
|
3
3
|
def self.to_map(excludes = {})
|
4
4
|
Hash[
|
5
5
|
ENV.keys.
|
6
|
-
|
7
|
-
|
8
|
-
].reject { |(k,_)| excludes.include?(k) }
|
6
|
+
select { |k| k.start_with?("SUTURE_") }.
|
7
|
+
map { |k| [to_sym(k), sanitize_value(ENV[k])] }
|
8
|
+
].reject { |(k, _)| excludes.include?(k) }
|
9
9
|
end
|
10
10
|
|
11
11
|
# private
|
12
12
|
|
13
13
|
def self.to_sym(name)
|
14
|
-
name.gsub(/^SUTURE\_/,
|
14
|
+
name.gsub(/^SUTURE\_/, "").downcase.to_sym
|
15
15
|
end
|
16
16
|
|
17
17
|
def self.sanitize_value(value)
|
data/lib/suture/util/scalpel.rb
CHANGED
@@ -10,7 +10,7 @@ module Suture::Util
|
|
10
10
|
plan.send(location).call(*args).tap do |result|
|
11
11
|
call_after_hook(plan, location, args, result)
|
12
12
|
end
|
13
|
-
rescue
|
13
|
+
rescue => error
|
14
14
|
log_error_details(plan, location, args, error)
|
15
15
|
call_error_hook(plan, location, args, error)
|
16
16
|
raise error
|
@@ -20,13 +20,13 @@ module Suture::Util
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def call_after_hook(plan, location, args, result)
|
23
|
-
return unless after_hook = try(plan, "after_#{location}")
|
23
|
+
return unless (after_hook = try(plan, "after_#{location}"))
|
24
24
|
after_hook.call(args, result)
|
25
25
|
end
|
26
26
|
|
27
27
|
def log_error_details(plan, location, args, error)
|
28
28
|
return if expected_error?(plan, error)
|
29
|
-
log_error <<-MSG.gsub(/^ {8}/,
|
29
|
+
log_error <<-MSG.gsub(/^ {8}/, "")
|
30
30
|
Suture invoked the #{plan.name.inspect} seam's #{location.inspect} code path with args: ```
|
31
31
|
#{args.inspect}
|
32
32
|
```
|
@@ -38,12 +38,12 @@ module Suture::Util
|
|
38
38
|
|
39
39
|
def call_error_hook(plan, location, args, error)
|
40
40
|
return if expected_error?(plan, error)
|
41
|
-
return unless error_hook = try(plan, "on_#{location}_error")
|
41
|
+
return unless (error_hook = try(plan, "on_#{location}_error"))
|
42
42
|
error_hook.call(plan.name, args, error)
|
43
43
|
end
|
44
44
|
|
45
45
|
def expected_error?(plan, error)
|
46
|
-
plan.expected_error_types.any? {|e| error.
|
46
|
+
plan.expected_error_types.any? {|e| error.is_a?(e) }
|
47
47
|
end
|
48
48
|
|
49
49
|
def args_for(plan, args_override)
|
@@ -62,6 +62,5 @@ module Suture::Util
|
|
62
62
|
return unless plan.respond_to?(method)
|
63
63
|
plan.send(method)
|
64
64
|
end
|
65
|
-
|
66
65
|
end
|
67
66
|
end
|
data/lib/suture/util/shuffle.rb
CHANGED
data/lib/suture/value/plan.rb
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
module Suture::Value
|
2
2
|
class Plan
|
3
3
|
attr_reader :name, :old, :new, :args, :after_new, :after_old, :on_new_error,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
:on_old_error, :database_path, :record_calls, :comparator,
|
5
|
+
:call_both, :raise_on_result_mismatch,
|
6
|
+
:return_old_on_result_mismatch, :fallback_on_error,
|
7
|
+
:expected_error_types, :disable, :dup_args
|
8
8
|
|
9
9
|
def initialize(attrs = {})
|
10
10
|
@name = attrs[:name]
|
data/lib/suture/value/result.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
module Suture::Value
|
2
2
|
class TestPlan
|
3
3
|
attr_accessor :name, :subject,
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
:verify_only, :fail_fast, :call_limit, :time_limit,
|
5
|
+
:error_message_limit, :random_seed, :comparator,
|
6
|
+
:database_path, :after_subject, :on_subject_error,
|
7
|
+
:expected_error_types
|
8
8
|
|
9
9
|
def initialize(attrs = {})
|
10
10
|
assign_simple_ivars!(attrs, :name, :subject, :comparator,
|
11
|
-
|
12
|
-
|
11
|
+
:database_path, :after_subject,
|
12
|
+
:on_subject_error)
|
13
13
|
assign_integral_ivars!(attrs, :verify_only, :call_limit, :time_limit,
|
14
|
-
|
14
|
+
:error_message_limit)
|
15
15
|
@fail_fast = !!attrs[:fail_fast]
|
16
16
|
@expected_error_types = attrs[:expected_error_types] || []
|
17
17
|
@random_seed = determine_random_seed(attrs)
|
@@ -26,18 +26,18 @@ module Suture::Value
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def assign_integral_ivars!(attrs, *names)
|
29
|
-
assign_simple_ivars!(massage_values(attrs, names
|
29
|
+
assign_simple_ivars!(massage_values(attrs, names, &:to_i), *names)
|
30
30
|
end
|
31
31
|
|
32
|
-
def massage_values(attrs, names
|
32
|
+
def massage_values(attrs, names)
|
33
33
|
Hash[
|
34
|
-
attrs.select {|(k,_)| names.include?(k) }.
|
35
|
-
|
34
|
+
attrs.select {|(k, _)| names.include?(k) }.
|
35
|
+
map { |(k, v)| [k, v.nil? ? nil : yield(v)]}
|
36
36
|
]
|
37
37
|
end
|
38
38
|
|
39
39
|
def determine_random_seed(attrs)
|
40
|
-
if attrs.
|
40
|
+
if attrs.key?(:random_seed)
|
41
41
|
if attrs[:random_seed].nil? || attrs[:random_seed] == "nil"
|
42
42
|
nil
|
43
43
|
else
|
data/lib/suture/verify.rb
CHANGED
@@ -14,17 +14,17 @@ module Suture
|
|
14
14
|
result = Value::Result.returned(@scalpel.cut(test_plan, :subject, observation.args))
|
15
15
|
{
|
16
16
|
:new_result => result,
|
17
|
-
:passed => compares_results.compare(observation.result, result)
|
17
|
+
:passed => compares_results.compare(observation.result, result),
|
18
18
|
}
|
19
|
-
rescue
|
19
|
+
rescue => error
|
20
20
|
if observation.result.errored?
|
21
21
|
result = Value::Result.errored(error)
|
22
22
|
{
|
23
23
|
:new_result => result,
|
24
|
-
:passed => compares_results.compare(observation.result, result)
|
24
|
+
:passed => compares_results.compare(observation.result, result),
|
25
25
|
}
|
26
26
|
else
|
27
|
-
{
|
27
|
+
{:error => error, :passed => false}
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
@@ -4,7 +4,7 @@ require "suture/util/env"
|
|
4
4
|
module Suture
|
5
5
|
class PrescribesTestPlan
|
6
6
|
UN_ENV_IABLE_OPTIONS = [:name, :subject, :comparator, :after_subject,
|
7
|
-
:on_subject_error, :expected_error_types]
|
7
|
+
:on_subject_error, :expected_error_types,]
|
8
8
|
|
9
9
|
def prescribe(name, options = {})
|
10
10
|
Value::TestPlan.new(Suture.config.
|
@@ -24,12 +24,12 @@ module Suture
|
|
24
24
|
if should_skip?(test_plan, experienced_failure_in_life, i, timer)
|
25
25
|
{
|
26
26
|
:observation => observation,
|
27
|
-
:ran => false
|
27
|
+
:ran => false,
|
28
28
|
}
|
29
29
|
else
|
30
30
|
@administers_test.administer(test_plan, observation).merge({
|
31
31
|
:observation => observation,
|
32
|
-
:ran => true
|
32
|
+
:ran => true,
|
33
33
|
}).tap { |r| experienced_failure_in_life = true unless r[:passed] }
|
34
34
|
end
|
35
35
|
})
|
@@ -39,7 +39,7 @@ module Suture
|
|
39
39
|
|
40
40
|
def validate_test_plan!(test_plan)
|
41
41
|
if !test_plan.subject || !test_plan.subject.respond_to?(:call)
|
42
|
-
raise Suture::Error::InvalidTestPlan
|
42
|
+
raise Suture::Error::InvalidTestPlan
|
43
43
|
end
|
44
44
|
end
|
45
45
|
|
@@ -56,7 +56,7 @@ module Suture
|
|
56
56
|
test_plan.random_seed
|
57
57
|
).tap do |test_cases|
|
58
58
|
next if test_cases.size > 0
|
59
|
-
log_warn <<-MSG.gsub(/^ {10}/,
|
59
|
+
log_warn <<-MSG.gsub(/^ {10}/, "")
|
60
60
|
Suture.verify found no recorded calls for seam #{test_plan.name.inspect}.
|
61
61
|
As a result, verify will have no effect and cannot provide any assurance
|
62
62
|
that the subject is working as expected.
|
@@ -70,4 +70,3 @@ module Suture
|
|
70
70
|
end
|
71
71
|
end
|
72
72
|
end
|
73
|
-
|
data/lib/suture/version.rb
CHANGED
data/lib/suture/wrap/logger.rb
CHANGED
@@ -12,12 +12,12 @@ module Suture::Wrap
|
|
12
12
|
end
|
13
13
|
|
14
14
|
@logger.level = if options[:log_level]
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
15
|
+
::Logger.const_get(options[:log_level])
|
16
|
+
else
|
17
|
+
::Logger::INFO
|
18
|
+
end
|
19
19
|
|
20
|
-
@logger.formatter = proc { |_, time
|
20
|
+
@logger.formatter = proc { |_, time, _, msg|
|
21
21
|
formatted_time = time.strftime("%Y-%m-%dT%H:%M:%S.%6N")
|
22
22
|
"[#{formatted_time}] Suture: #{msg}\n".tap { |out|
|
23
23
|
puts out if options[:log_stdout]
|
@@ -25,19 +25,39 @@ module Suture::Wrap
|
|
25
25
|
}
|
26
26
|
}
|
27
27
|
|
28
|
-
|
28
|
+
@logger
|
29
29
|
end
|
30
30
|
|
31
31
|
class NullIO
|
32
|
-
def gets
|
33
|
-
|
34
|
-
|
35
|
-
def
|
36
|
-
|
37
|
-
|
38
|
-
def
|
39
|
-
|
40
|
-
|
32
|
+
def gets
|
33
|
+
end
|
34
|
+
|
35
|
+
def each
|
36
|
+
end
|
37
|
+
|
38
|
+
def read(count = nil, _buffer = nil)
|
39
|
+
count && count > 0 ? nil : ""
|
40
|
+
end
|
41
|
+
|
42
|
+
def rewind
|
43
|
+
0
|
44
|
+
end
|
45
|
+
|
46
|
+
def close
|
47
|
+
end
|
48
|
+
|
49
|
+
def size
|
50
|
+
0
|
51
|
+
end
|
52
|
+
|
53
|
+
def sync=(*args)
|
54
|
+
end
|
55
|
+
|
56
|
+
def puts(*args)
|
57
|
+
end
|
58
|
+
|
59
|
+
def write(*args)
|
60
|
+
end
|
41
61
|
end
|
42
62
|
end
|
43
63
|
end
|
data/lib/suture/wrap/sqlite.rb
CHANGED
data/suture.gemspec
CHANGED
@@ -1,7 +1,8 @@
|
|
1
1
|
# coding: utf-8
|
2
|
-
|
2
|
+
|
3
|
+
lib = File.expand_path("../lib", __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require
|
5
|
+
require "suture/version"
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
8
|
spec.name = "suture"
|
@@ -9,8 +10,8 @@ Gem::Specification.new do |spec|
|
|
9
10
|
spec.authors = ["Justin Searls"]
|
10
11
|
spec.email = ["searls@gmail.com"]
|
11
12
|
|
12
|
-
spec.summary =
|
13
|
-
spec.description =
|
13
|
+
spec.summary = "A gem that helps people refactor or reimplement legacy code"
|
14
|
+
spec.description = "Provides tools to record calls to legacy code and verify new implementations still work"
|
14
15
|
spec.homepage = "https://github.com/testdouble/suture"
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(example|test|db|log|safe|spec|features)/}) }
|
@@ -30,7 +31,11 @@ Gem::Specification.new do |spec|
|
|
30
31
|
|
31
32
|
if Gem.ruby_version >= Gem::Version.new("1.9.3")
|
32
33
|
spec.add_development_dependency "codeclimate-test-reporter"
|
33
|
-
spec.add_development_dependency "simplecov", "~> 0.11.2"
|
34
|
+
spec.add_development_dependency "simplecov", "~> 0.11.2" # <--only here to lock
|
35
|
+
end
|
36
|
+
|
37
|
+
if Gem.ruby_version >= Gem::Version.new("2.2.0")
|
38
|
+
spec.add_development_dependency "standard"
|
34
39
|
end
|
35
40
|
|
36
41
|
if Gem.ruby_version >= Gem::Version.new("2.2.2")
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suture
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Justin Searls
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2018-11-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sqlite3
|
@@ -150,6 +150,20 @@ dependencies:
|
|
150
150
|
- - "~>"
|
151
151
|
- !ruby/object:Gem::Version
|
152
152
|
version: 0.11.2
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: standard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - ">="
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - ">="
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
153
167
|
- !ruby/object:Gem::Dependency
|
154
168
|
name: github_changelog_generator
|
155
169
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,6 +190,7 @@ files:
|
|
176
190
|
- ".gitignore"
|
177
191
|
- ".projections.json"
|
178
192
|
- ".rubocop.yml"
|
193
|
+
- ".standard.yml"
|
179
194
|
- ".travis.yml"
|
180
195
|
- CHANGELOG.md
|
181
196
|
- Gemfile
|
@@ -246,7 +261,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
246
261
|
version: '0'
|
247
262
|
requirements: []
|
248
263
|
rubyforge_project:
|
249
|
-
rubygems_version: 2.
|
264
|
+
rubygems_version: 2.7.6
|
250
265
|
signing_key:
|
251
266
|
specification_version: 4
|
252
267
|
summary: A gem that helps people refactor or reimplement legacy code
|