suture 1.1.1 → 1.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|