vcr 2.0.0.beta1 → 2.0.0.beta2

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.
Files changed (88) hide show
  1. data/.gitignore +1 -0
  2. data/.travis.yml +3 -0
  3. data/CHANGELOG.md +37 -2
  4. data/Gemfile +2 -2
  5. data/README.md +10 -1
  6. data/Rakefile +43 -7
  7. data/Upgrade.md +45 -0
  8. data/features/.nav +1 -0
  9. data/features/cassettes/automatic_re_recording.feature +19 -17
  10. data/features/cassettes/dynamic_erb.feature +32 -28
  11. data/features/cassettes/exclusive.feature +28 -24
  12. data/features/cassettes/format.feature +213 -31
  13. data/features/cassettes/update_content_length_header.feature +20 -18
  14. data/features/configuration/filter_sensitive_data.feature +4 -4
  15. data/features/configuration/hooks.feature +27 -23
  16. data/features/http_libraries/em_http_request.feature +79 -75
  17. data/features/record_modes/all.feature +14 -14
  18. data/features/record_modes/new_episodes.feature +15 -15
  19. data/features/record_modes/none.feature +15 -15
  20. data/features/record_modes/once.feature +15 -15
  21. data/features/request_matching/body.feature +25 -23
  22. data/features/request_matching/custom_matcher.feature +25 -23
  23. data/features/request_matching/headers.feature +32 -36
  24. data/features/request_matching/host.feature +27 -25
  25. data/features/request_matching/identical_request_sequence.feature +27 -25
  26. data/features/request_matching/method.feature +27 -25
  27. data/features/request_matching/path.feature +27 -25
  28. data/features/request_matching/playback_repeats.feature +27 -25
  29. data/features/request_matching/uri.feature +27 -25
  30. data/features/request_matching/uri_without_param.feature +28 -26
  31. data/features/step_definitions/cli_steps.rb +71 -17
  32. data/features/support/env.rb +3 -1
  33. data/features/support/http_lib_filters.rb +6 -3
  34. data/features/support/vcr_cucumber_helpers.rb +4 -2
  35. data/lib/vcr.rb +6 -2
  36. data/lib/vcr/cassette.rb +75 -51
  37. data/lib/vcr/cassette/migrator.rb +111 -0
  38. data/lib/vcr/cassette/serializers.rb +35 -0
  39. data/lib/vcr/cassette/serializers/json.rb +23 -0
  40. data/lib/vcr/cassette/serializers/psych.rb +24 -0
  41. data/lib/vcr/cassette/serializers/syck.rb +35 -0
  42. data/lib/vcr/cassette/serializers/yaml.rb +24 -0
  43. data/lib/vcr/configuration.rb +6 -1
  44. data/lib/vcr/errors.rb +1 -1
  45. data/lib/vcr/library_hooks/excon.rb +1 -7
  46. data/lib/vcr/library_hooks/typhoeus.rb +6 -22
  47. data/lib/vcr/library_hooks/webmock.rb +1 -1
  48. data/lib/vcr/middleware/faraday.rb +1 -1
  49. data/lib/vcr/request_matcher_registry.rb +43 -30
  50. data/lib/vcr/structs.rb +209 -0
  51. data/lib/vcr/tasks/vcr.rake +9 -0
  52. data/lib/vcr/version.rb +1 -1
  53. data/spec/fixtures/cassette_spec/1_x_cassette.yml +110 -0
  54. data/spec/fixtures/cassette_spec/example.yml +79 -78
  55. data/spec/fixtures/cassette_spec/with_localhost_requests.yml +79 -77
  56. data/spec/fixtures/fake_example.com_responses.yml +78 -76
  57. data/spec/fixtures/match_requests_on.yml +147 -145
  58. data/spec/monkey_patches.rb +5 -5
  59. data/spec/support/http_library_adapters.rb +48 -0
  60. data/spec/support/shared_example_groups/hook_into_http_library.rb +53 -20
  61. data/spec/support/sinatra_app.rb +12 -0
  62. data/spec/vcr/cassette/http_interaction_list_spec.rb +1 -1
  63. data/spec/vcr/cassette/migrator_spec.rb +183 -0
  64. data/spec/vcr/cassette/serializers_spec.rb +122 -0
  65. data/spec/vcr/cassette_spec.rb +147 -83
  66. data/spec/vcr/configuration_spec.rb +11 -1
  67. data/spec/vcr/library_hooks/typhoeus_spec.rb +3 -3
  68. data/spec/vcr/library_hooks/webmock_spec.rb +7 -1
  69. data/spec/vcr/request_ignorer_spec.rb +1 -1
  70. data/spec/vcr/request_matcher_registry_spec.rb +46 -4
  71. data/spec/vcr/structs_spec.rb +309 -0
  72. data/spec/vcr_spec.rb +7 -0
  73. data/vcr.gemspec +9 -12
  74. metadata +75 -61
  75. data/lib/vcr/structs/http_interaction.rb +0 -58
  76. data/lib/vcr/structs/normalizers/body.rb +0 -24
  77. data/lib/vcr/structs/normalizers/header.rb +0 -64
  78. data/lib/vcr/structs/normalizers/status_message.rb +0 -17
  79. data/lib/vcr/structs/normalizers/uri.rb +0 -34
  80. data/lib/vcr/structs/request.rb +0 -13
  81. data/lib/vcr/structs/response.rb +0 -13
  82. data/lib/vcr/structs/response_status.rb +0 -5
  83. data/lib/vcr/util/yaml.rb +0 -11
  84. data/spec/support/shared_example_groups/normalizers.rb +0 -94
  85. data/spec/vcr/structs/http_interaction_spec.rb +0 -89
  86. data/spec/vcr/structs/request_spec.rb +0 -39
  87. data/spec/vcr/structs/response_spec.rb +0 -44
  88. data/spec/vcr/structs/response_status_spec.rb +0 -9
data/.gitignore CHANGED
@@ -31,5 +31,6 @@ Gemfile.lock
31
31
  features/README.md
32
32
  features/CHANGELOG.md
33
33
  features/LICENSE.md
34
+ features/Upgrade.md
34
35
 
35
36
  .rvmrc
data/.travis.yml CHANGED
@@ -7,8 +7,11 @@ rvm:
7
7
  - 1.9.3
8
8
  - ree
9
9
  - jruby
10
+ - rbx
11
+ - rbx-2.0
10
12
  branches:
11
13
  only:
12
14
  - master
13
15
  - 1-x-stable
16
+ - travis-testing
14
17
 
data/CHANGELOG.md CHANGED
@@ -1,12 +1,47 @@
1
1
  ## In git
2
2
 
3
- [Full Changelog](http://github.com/myronmarston/vcr/compare/v2.0.0.beta1...master)
3
+ [Full Changelog](http://github.com/myronmarston/vcr/compare/v2.0.0.beta2...master)
4
+
5
+ ## 2.0.0 Beta 2 (November 6, 2011)
6
+
7
+ [Full Changelog](http://github.com/myronmarston/vcr/compare/v2.0.0.beta1...v2.0.0.beta2)
8
+
9
+ * Update to (and require) Typhoeus 0.3.2.
10
+ * Fix a bug with `VCR.request_matchers.uri_without_param(:some_param)`
11
+ so that it properly handles URIs that have no parameters. Thanks to
12
+ [Sathya Sekaran](https://github.com/sfsekaran) for this fix.
13
+ * The cassette format has changed significantly:
14
+ * The HTTPInteractions are no longer normalized in a lossy fashion.
15
+ VCR 1.x converted all HTTP header keys to lowercase. VCR 2.0 no
16
+ longer does this because it is impossible to know what the original
17
+ casing was (i.e. given `etag`, was it originally `etag`, `ETag` or
18
+ `Etag`?). Also, some HTTP libraries add particular request headers
19
+ to every request, and these used to be ignored. The aren't anymore.
20
+ * The ruby struct objects are not directly serialized anymore.
21
+ Instead, only primitives (hashes, arrays, strings, integers) are
22
+ serialized. This allows swappable serializers and will allow other
23
+ tools to read and use a VCR cassette.
24
+ * Add new serializer API. VCR ships with YAML, Syck, Psych and JSON
25
+ serializers, and it is very simple to implement your own. The
26
+ serializer can be configured on a per-cassette basis.
27
+ * New `vcr:migrate_cassettes DIR=path/to/cassettes` rake task assists
28
+ with upgrading from VCR 1.x to 2.0.
29
+ * Cassettes now contain a `recorded_with` attribute. This should
30
+ allow the cassette structure to be updated more easily in the future
31
+ as the version number provides a means for easily migrating
32
+ cassettes.
33
+ * Add `recorded_at` to data serialized with an HTTPInteraction. This
34
+ allows the `:re_record_interval` cassette option to work more
35
+ accurately and no longer rely on the file modification time.
36
+
37
+ Note that VCR 1.x cassettes cannot be used with VCR 2.0. See the
38
+ upgrade notes for more info.
4
39
 
5
40
  ## 2.0.0 Beta 1 (October 8, 2011)
6
41
 
7
42
  [Full Changelog](http://github.com/myronmarston/vcr/compare/v1.11.3...v2.0.0.beta1)
8
43
 
9
- ### Changes
44
+ ### Changed
10
45
 
11
46
  * Previously, the last matching response in a cassette would
12
47
  repeatedly playback if the same request kept being made. This is
data/Gemfile CHANGED
@@ -6,8 +6,8 @@ group :development do
6
6
  platforms :ruby do
7
7
  gem 'patron', '~> 0.4.15'
8
8
  gem 'em-http-request', '~> 0.3.0'
9
- gem 'curb', '~> 0.7.15'
10
- gem 'typhoeus', '~> 0.2.1'
9
+ gem 'curb', '0.7.15'
10
+ gem 'typhoeus', '~> 0.3.2'
11
11
  end
12
12
 
13
13
  platforms :jruby do
data/README.md CHANGED
@@ -135,6 +135,7 @@ If you find VCR useful, please recommend me on [working with rails](http://worki
135
135
  Thanks also to the following people who have contributed patches or helpful suggestions:
136
136
 
137
137
  * [Aaron Brethorst](http://github.com/aaronbrethorst)
138
+ * [Avdi Grimm](https://github.com/avdi)
138
139
  * [Bartosz Blimke](http://github.com/bblimke)
139
140
  * [Ben Hutton](http://github.com/benhutton)
140
141
  * [Bradley Isotope](https://github.com/bradleyisotope)
@@ -143,8 +144,16 @@ Thanks also to the following people who have contributed patches or helpful sugg
143
144
  * [Karl Baum](https://github.com/kbaum)
144
145
  * [Nathaniel Bibler](https://github.com/nbibler)
145
146
  * [Oliver Searle-Barnes](https://github.com/opsb)
147
+ * [Sathya Sekaran](https://github.com/sfsekaran)
146
148
  * [Wesley Beary](https://github.com/geemus)
147
- * [Avdi Grimm](https://github.com/avdi)
149
+
150
+ ## Similar Libraries
151
+
152
+ * [Betamax](https://github.com/robfletcher/betamax) (Groovy)
153
+ * [Ephemeral Response](https://github.com/sandro/ephemeral_response) (Ruby)
154
+ * [Mimic](https://github.com/acoulton/mimic) (PHP/Kohana)
155
+ * [NetRecorder](https://github.com/chrisyoung/netrecorder) (Ruby)
156
+ * [Stale Fish](https://github.com/jsmestad/stale_fish) (Ruby)
148
157
 
149
158
  ## Copyright
150
159
 
data/Rakefile CHANGED
@@ -15,10 +15,6 @@ RSpec::Core::RakeTask.new(:spec) do |t|
15
15
  # we require spec_helper so we don't get an RSpec warning about
16
16
  # examples being defined before configuration.
17
17
  t.ruby_opts = "-w -I./spec -r./spec/capture_warnings -rspec_helper"
18
-
19
- # I'm not sure why, but bundler seems to silence warnings...
20
- t.skip_bundler = true
21
-
22
18
  t.rspec_opts = %w[--format progress] if (ENV['FULL_BUILD'] || !using_git)
23
19
  end
24
20
 
@@ -51,8 +47,6 @@ namespace :ci do
51
47
  # we require spec_helper so we don't get an RSpec warning about
52
48
  # examples being defined before configuration.
53
49
  t.ruby_opts = "-w -I./spec -r./spec/capture_warnings -rspec_helper"
54
- # I'm not sure why, but bundler seems to silence warnings...
55
- t.skip_bundler = true
56
50
  t.rspec_opts = %w[--format progress --backtrace]
57
51
  end
58
52
 
@@ -78,7 +72,7 @@ end
78
72
 
79
73
  desc "Push cukes to relishapp using the relish-client-gem"
80
74
  task :relish do
81
- %w[ README.md CHANGELOG.md LICENSE ].each do |file|
75
+ %w[ README.md CHANGELOG.md Upgrade.md LICENSE ].each do |file|
82
76
  ensure_relish_doc_symlinked(file)
83
77
  end
84
78
 
@@ -100,3 +94,45 @@ task :release => [:require_ruby_18, :prep_relish_release, :relish]
100
94
  # For gem-test: http://gem-testers.org/
101
95
  task :test => :spec
102
96
 
97
+ load './lib/vcr/tasks/vcr.rake'
98
+ namespace :vcr do
99
+ task :reset_spec_cassettes do
100
+ ENV['DIR'] = 'spec/fixtures'
101
+ def VCR.version; "2.0.0"; end
102
+ sh "git checkout v2.0.0.beta1 -- spec/fixtures"
103
+ end
104
+
105
+ task :migrate_cassettes => :reset_spec_cassettes
106
+ end
107
+
108
+ desc "Migrate cucumber cassettes"
109
+ task :migrate_cucumber_cassettes do
110
+ sh "git checkout cb3559d6ffcb36cb823ae96a677e380e5b86ed80 -- features"
111
+ require 'vcr/cassette/migrator'
112
+ Dir["features/**/*.feature"].each do |feature_file|
113
+ puts " - Migrating #{feature_file}"
114
+ contents = File.read(feature_file)
115
+
116
+ # http://rubular.com/r/gjzkoaYX2O
117
+ contents.scan(/:\n^\s+"""\n([\s\S]+?)"""/).each do |captures|
118
+ capture = captures.first
119
+ indentation = capture[/^ +/]
120
+ cassette_yml = capture.gsub(/^#{indentation}/, '')
121
+ new_yml = nil
122
+
123
+ Dir.mktmpdir do |dir|
124
+ file_name = "#{dir}/cassette.yml"
125
+ File.open(file_name, 'w') { |f| f.write(cassette_yml) }
126
+ VCR::Cassette::Migrator.new(dir, StringIO.new).migrate!
127
+ new_yml = File.read(file_name)
128
+ end
129
+
130
+ new_yml.gsub!(/^/, indentation)
131
+ new_yml << indentation
132
+ contents.gsub!(capture, new_yml)
133
+ end
134
+
135
+ File.open(feature_file, 'w') { |f| f.write(contents) }
136
+ end
137
+ end
138
+
data/Upgrade.md ADDED
@@ -0,0 +1,45 @@
1
+ See the [Changelog](changelog) for a complete list of changes from VCR
2
+ 1.x to 2.0. This file simply lists the most pertinent ones to upgrading.
3
+
4
+ ## Configuration Changes
5
+
6
+ In VCR 1.x, your configuration block would be something like this:
7
+
8
+ ``` ruby
9
+ VCR.config do |c|
10
+ c.cassette_library_dir = 'cassettes'
11
+ c.stub_with :fakeweb, :typhoeus
12
+ end
13
+ ```
14
+
15
+ This will continue to work in VCR 2.0 but will generate deprecation
16
+ warnings. Instead, you should change this to:
17
+
18
+ ``` ruby
19
+ VCR.configure do |c|
20
+ c.cassette_library_dir = 'cassettes'
21
+ c.hook_into :fakeweb, :typhoeus
22
+ end
23
+ ```
24
+
25
+ ## New Cassette Format
26
+
27
+ The cassette format has changed between VCR 1.x and VCR 2.0.
28
+ VCR 1.x cassettes cannot be used with VCR 2.0.
29
+
30
+ The easiest way to upgrade is to simply delete your cassettes and
31
+ re-record all of them. VCR also provides a rake task that attempts
32
+ to upgrade your 1.x cassettes to the new 2.0 format. To use it, add
33
+ the following line to your Rakefile:
34
+
35
+ ``` ruby
36
+ load 'vcr/tasks/vcr.rake'
37
+ ```
38
+
39
+ Then run `rake vcr:migrate_cassettes DIR=path/to/your/cassettes/directory` to
40
+ upgrade your cassettes. Note that this rake task may be unable to
41
+ upgrade some cassettes that make extensive use of ERB. In addition, now
42
+ that VCR 2.0 does less normalization then before, it may not be able to
43
+ migrate the cassette perfectly. It's recommended that you delete and
44
+ re-record your cassettes if you are able.
45
+
data/features/.nav CHANGED
@@ -1,4 +1,5 @@
1
1
  - getting_started.md (Getting Started)
2
+ - Upgrade.md (Upgrade)
2
3
  - CHANGELOG.md (Changelog)
3
4
  - about_these_examples.md (About These Examples)
4
5
  - LICENSE.md (License)
@@ -6,29 +6,31 @@ Feature: Automatic Re-recording
6
6
 
7
7
  The value provided should be an interval (expressed in seconds) that
8
8
  determines how often VCR will re-record the cassette. When a cassette
9
- is used, VCR checks the file modification time; if more time than the
10
- interval has passed, VCR will use the `:all` record mode to cause it be
11
- re-recorded.
9
+ is used, VCR checks the earliest `recorded_at` timestamp in the cassette;
10
+ if more time than the interval has passed since that timestamp,
11
+ VCR will use the `:all` record mode to cause it be re-recorded.
12
12
 
13
13
  Background:
14
14
  Given a previously recorded cassette file "cassettes/example.yml" with:
15
15
  """
16
- ---
17
- - !ruby/struct:VCR::HTTPInteraction
18
- request: !ruby/struct:VCR::Request
19
- method: :get
16
+ ---
17
+ http_interactions:
18
+ - request:
19
+ method: get
20
20
  uri: http://localhost:7777/
21
- body:
22
- headers:
23
- response: !ruby/struct:VCR::Response
24
- status: !ruby/struct:VCR::ResponseStatus
21
+ body: ''
22
+ headers: {}
23
+ response:
24
+ status:
25
25
  code: 200
26
26
  message: OK
27
- headers:
28
- content-length:
29
- - "12"
27
+ headers:
28
+ Content-Length:
29
+ - '12'
30
30
  body: Old Response
31
- http_version: "1.1"
31
+ http_version: '1.1'
32
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
33
+ recorded_with: VCR 2.0.0
32
34
  """
33
35
  And a file named "re_record.rb" with:
34
36
  """ruby
@@ -49,7 +51,7 @@ Feature: Automatic Re-recording
49
51
  """
50
52
 
51
53
  Scenario: Cassette is not re-recorded when not enough time has passed
52
- Given 6 days have passed since the cassette was recorded
54
+ Given it is Tue, 07 Nov 2011
53
55
  When I run `ruby re_record.rb`
54
56
  Then the output should contain "Old Response"
55
57
  But the output should not contain "New Response"
@@ -57,7 +59,7 @@ Feature: Automatic Re-recording
57
59
  But the file "cassettes/example.yml" should not contain "body: New Response"
58
60
 
59
61
  Scenario: Cassette is re-recorded when enough time has passed
60
- Given 8 days have passed since the cassette was recorded
62
+ Given it is Tue, 09 Nov 2011
61
63
  When I run `ruby re_record.rb`
62
64
  Then the output should contain "New Response"
63
65
  But the output should not contain "Old Response"
@@ -12,24 +12,26 @@ Feature: Dynamic ERB cassettes
12
12
  Scenario: Enable dynamic ERB cassette evalutation using :erb => true
13
13
  Given a previously recorded cassette file "cassettes/dynamic.yml" with:
14
14
  """
15
- ---
16
- - !ruby/struct:VCR::HTTPInteraction
17
- request: !ruby/struct:VCR::Request
18
- method: :get
19
- uri: http://example.com:80/foo?a=<%= 'b' * 3 %>
20
- body:
21
- headers:
22
- response: !ruby/struct:VCR::Response
23
- status: !ruby/struct:VCR::ResponseStatus
15
+ ---
16
+ http_interactions:
17
+ - request:
18
+ method: get
19
+ uri: http://example.com/foo?a=<%= 'b' * 3 %>
20
+ body: ''
21
+ headers: {}
22
+ response:
23
+ status:
24
24
  code: 200
25
25
  message: OK
26
- headers:
27
- content-type:
26
+ headers:
27
+ Content-Type:
28
28
  - text/html;charset=utf-8
29
- content-length:
30
- - "9"
29
+ Content-Length:
30
+ - '9'
31
31
  body: Hello <%= 'bar'.next %>
32
- http_version: "1.1"
32
+ http_version: '1.1'
33
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
34
+ recorded_with: VCR 2.0.0
33
35
  """
34
36
  And a file named "dynamic_erb_example.rb" with:
35
37
  """ruby
@@ -51,24 +53,26 @@ Feature: Dynamic ERB cassettes
51
53
  Scenario: Pass arguments to the ERB using :erb => { ... }
52
54
  Given a previously recorded cassette file "cassettes/dynamic.yml" with:
53
55
  """
54
- ---
55
- - !ruby/struct:VCR::HTTPInteraction
56
- request: !ruby/struct:VCR::Request
57
- method: :get
58
- uri: http://example.com:80/foo?a=<%= arg1 %>
59
- body:
60
- headers:
61
- response: !ruby/struct:VCR::Response
62
- status: !ruby/struct:VCR::ResponseStatus
56
+ ---
57
+ http_interactions:
58
+ - request:
59
+ method: get
60
+ uri: http://example.com/foo?a=<%= arg1 %>
61
+ body: ''
62
+ headers: {}
63
+ response:
64
+ status:
63
65
  code: 200
64
66
  message: OK
65
- headers:
66
- content-type:
67
+ headers:
68
+ Content-Type:
67
69
  - text/html;charset=utf-8
68
- content-length:
69
- - "9"
70
+ Content-Length:
71
+ - '9'
70
72
  body: Hello <%= arg2 %>
71
- http_version: "1.1"
73
+ http_version: '1.1'
74
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
75
+ recorded_with: VCR 2.0.0
72
76
  """
73
77
  And a file named "dynamic_erb_example.rb" with:
74
78
  """ruby
@@ -17,41 +17,45 @@ Feature: exclusive cassette
17
17
  Background:
18
18
  Given a previously recorded cassette file "cassettes/outer.yml" with:
19
19
  """
20
- ---
21
- - !ruby/struct:VCR::HTTPInteraction
22
- request: !ruby/struct:VCR::Request
23
- method: :get
20
+ ---
21
+ http_interactions:
22
+ - request:
23
+ method: get
24
24
  uri: http://localhost:7777/outer
25
- body:
26
- headers:
27
- response: !ruby/struct:VCR::Response
28
- status: !ruby/struct:VCR::ResponseStatus
25
+ body: ''
26
+ headers: {}
27
+ response:
28
+ status:
29
29
  code: 200
30
30
  message: OK
31
- headers:
32
- content-length:
33
- - "18"
31
+ headers:
32
+ Content-Length:
33
+ - '18'
34
34
  body: Old outer response
35
- http_version: "1.1"
35
+ http_version: '1.1'
36
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
37
+ recorded_with: VCR 2.0.0
36
38
  """
37
39
  And a previously recorded cassette file "cassettes/inner.yml" with:
38
40
  """
39
- ---
40
- - !ruby/struct:VCR::HTTPInteraction
41
- request: !ruby/struct:VCR::Request
42
- method: :get
41
+ ---
42
+ http_interactions:
43
+ - request:
44
+ method: get
43
45
  uri: http://localhost:7777/inner
44
- body:
45
- headers:
46
- response: !ruby/struct:VCR::Response
47
- status: !ruby/struct:VCR::ResponseStatus
46
+ body: ''
47
+ headers: {}
48
+ response:
49
+ status:
48
50
  code: 200
49
51
  message: OK
50
- headers:
51
- content-length:
52
- - "18"
52
+ headers:
53
+ Content-Length:
54
+ - '18'
53
55
  body: Old inner response
54
- http_version: "1.1"
56
+ http_version: '1.1'
57
+ recorded_at: Tue, 01 Nov 2011 04:58:44 GMT
58
+ recorded_with: VCR 2.0.0
55
59
  """
56
60
  And a file named "setup.rb" with:
57
61
  """ruby