whisperer 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +7 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +293 -0
  8. data/Rakefile +1 -0
  9. data/TODO.md +47 -0
  10. data/lib/whisperer.rb +102 -0
  11. data/lib/whisperer/config.rb +40 -0
  12. data/lib/whisperer/convertors/hash.rb +39 -0
  13. data/lib/whisperer/convertors/interaction.rb +21 -0
  14. data/lib/whisperer/dsl.rb +19 -0
  15. data/lib/whisperer/dsl/base.rb +47 -0
  16. data/lib/whisperer/dsl/body.rb +33 -0
  17. data/lib/whisperer/dsl/headers.rb +20 -0
  18. data/lib/whisperer/dsl/request.rb +15 -0
  19. data/lib/whisperer/dsl/response.rb +15 -0
  20. data/lib/whisperer/dsl/response/status.rb +10 -0
  21. data/lib/whisperer/generator.rb +43 -0
  22. data/lib/whisperer/helpers.rb +16 -0
  23. data/lib/whisperer/placeholder.rb +14 -0
  24. data/lib/whisperer/preprocessors.rb +19 -0
  25. data/lib/whisperer/preprocessors/base.rb +13 -0
  26. data/lib/whisperer/preprocessors/content_length.rb +17 -0
  27. data/lib/whisperer/preprocessors/response_body.rb +23 -0
  28. data/lib/whisperer/record.rb +48 -0
  29. data/lib/whisperer/record/body.rb +15 -0
  30. data/lib/whisperer/record/headers.rb +22 -0
  31. data/lib/whisperer/record/request.rb +16 -0
  32. data/lib/whisperer/record/response.rb +18 -0
  33. data/lib/whisperer/record/response/status.rb +10 -0
  34. data/lib/whisperer/samples/cassette_builder.rb +24 -0
  35. data/lib/whisperer/serializers/base.rb +24 -0
  36. data/lib/whisperer/serializers/json.rb +33 -0
  37. data/lib/whisperer/serializers/json_multiple.rb +14 -0
  38. data/lib/whisperer/tasks/whisperer.rake +88 -0
  39. data/lib/whisperer/version.rb +3 -0
  40. data/spec/cassette_builders/arya_stark.rb +26 -0
  41. data/spec/cassette_builders/bran_stark.rb +19 -0
  42. data/spec/cassette_builders/empty_robb_stark.rb +20 -0
  43. data/spec/cassette_builders/robb_stark.rb +30 -0
  44. data/spec/cassette_builders/robb_stark_without_content_length.rb +20 -0
  45. data/spec/cassette_builders/sansa_stark.rb +9 -0
  46. data/spec/cassette_builders/starks.rb +31 -0
  47. data/spec/cassette_builders/wolfs.rb +7 -0
  48. data/spec/cassettes/empty_robb_stark.yml +22 -0
  49. data/spec/cassettes/girls/arya_stark.yml +24 -0
  50. data/spec/cassettes/robb_stark.yml +28 -0
  51. data/spec/cassettes/robb_stark_without_content_length.yml +22 -0
  52. data/spec/cassettes/sansa_stark.yml +24 -0
  53. data/spec/cassettes/starks.yml +28 -0
  54. data/spec/cassettes/wolfs.yml +28 -0
  55. data/spec/factories/arya_stark.rb +7 -0
  56. data/spec/factories/bran_stark.rb +7 -0
  57. data/spec/factories/ned_stark.rb +7 -0
  58. data/spec/factories/robb_stark.rb +7 -0
  59. data/spec/factories/sansa_stark.rb +7 -0
  60. data/spec/integration/whisperer_spec.rb +51 -0
  61. data/spec/spec_helper.rb +25 -0
  62. data/spec/spec_integration_helper.rb +6 -0
  63. data/spec/support/cassettes.rb +16 -0
  64. data/spec/support/custom_serializer.rb +5 -0
  65. data/spec/unit/config_spec.rb +94 -0
  66. data/spec/unit/convertors/hash_spec.rb +59 -0
  67. data/spec/unit/convertors/interaction_spec.rb +46 -0
  68. data/spec/unit/dsl/base_spec.rb +99 -0
  69. data/spec/unit/dsl/body_spec.rb +73 -0
  70. data/spec/unit/dsl/headers_spec.rb +31 -0
  71. data/spec/unit/dsl/request_spec.rb +4 -0
  72. data/spec/unit/dsl/response_spec.rb +4 -0
  73. data/spec/unit/dsl/status_spec.rb +4 -0
  74. data/spec/unit/dsl_spec.rb +4 -0
  75. data/spec/unit/generator_spec.rb +77 -0
  76. data/spec/unit/helpers_spec.rb +38 -0
  77. data/spec/unit/preprocessors/content_length_spec.rb +39 -0
  78. data/spec/unit/preprocessors/response_body_spec.rb +55 -0
  79. data/spec/unit/preprocessors_spec.rb +8 -0
  80. data/spec/unit/record/headers_spec.rb +21 -0
  81. data/spec/unit/record/response_spec.rb +11 -0
  82. data/spec/unit/record_spec.rb +77 -0
  83. data/spec/unit/serializers/base_spec.rb +19 -0
  84. data/spec/unit/serializers/json_multiple_spec.rb +24 -0
  85. data/spec/unit/serializers/json_spec.rb +37 -0
  86. data/spec/unit/whisperer_spec.rb +189 -0
  87. data/whisperer.gemspec +28 -0
  88. metadata +277 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NDliMDRjMTM4ZDRiMDMyZDIwMTc1Y2U1NjBiZWNjNzdhYWM2ZjY3ZQ==
5
+ data.tar.gz: !binary |-
6
+ YjQzMTBmMmMzZmIyYTQ4ZGRmZmZlM2ZiZTI1MjMwNjJmNGJkY2Q1Nw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MzllODJkNDVmYzgyZTQzYzYzZjBhMWM4YmE2NmMzOWEwOGU0YmI2ZTdmMTEw
10
+ YzU2Y2E2MTg2ZGI2Njc3NTQ0N2I2YTU2YmFhZDc4NzdiZDk3ZTI5ZTFkN2Ix
11
+ ZGU5ZjJhMzc4NDkwM2U4NDljNmUyNmU5YjY3YzY2NzZjM2M2NDE=
12
+ data.tar.gz: !binary |-
13
+ YTAwZjBlMzAwY2NmOTFlMmU1NGEwYzE4ODI3NWJmNjc0ZTIxNzNiMTAzMTNh
14
+ MTIxYzZiYTU4NzM2YzE5ZWZlNGFlMmIzNTIxOTYzN2U2NDNjMzIzNThmZDRm
15
+ N2NkZmJiZWIxMDQ5ZDdkMWEwNzI0ZThmYjg4OGFiMmM2Njg5YjI=
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --colour
2
+ --profile
data/.travis.yml ADDED
@@ -0,0 +1,13 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.1.4
4
+ - 2.1.3
5
+ - 2.1.2
6
+ - 2.1.1
7
+ - 2.1.0
8
+ - 2.0.0
9
+ matrix:
10
+ before_install:
11
+ - "export DISPLAY=:99.0"
12
+ - "sh -e /etc/init.d/xvfb start"
13
+ script: bundle exec rspec spec
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in whisperer.gemspec
4
+ gemspec
5
+
6
+ gem 'rspec', '~> 3.1'
7
+ gem 'rake'
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Dmitriy Nesteryuk
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,293 @@
1
+ # Whisperer
2
+
3
+ [![Code Climate](https://codeclimate.com/github/dnesteryuk/whisperer/badges/gpa.svg)](https://codeclimate.com/github/dnesteryuk/whisperer)
4
+ [![Build Status](https://secure.travis-ci.org/dnesteryuk/whisperer.png?branch=master)](https://travis-ci.org/dnesteryuk/whisperer)
5
+ [![Dependency Status](https://gemnasium.com/dnesteryuk/whisperer.png)](https://gemnasium.com/dnesteryuk/whisperer)
6
+
7
+ Do you hate fixtures? I do as well. The purpose of this library is to make your life easier when your application works with external API and you use VCR to stub that API.
8
+
9
+ ## Installation
10
+
11
+ **Requirments**:
12
+ - Ruby 2.0.x or 2.1.x
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ gem 'whisperer'
17
+
18
+ And then execute:
19
+
20
+ $ bundle
21
+
22
+ Or install it yourself as:
23
+
24
+ $ gem install whisperer
25
+
26
+ To create default directories' structure and the config file with default options, you need to execute:
27
+
28
+ $ rake whisperer:install
29
+
30
+ It will create `cassette_builders` directory in your `spec` folder and `.whisperer.yml` file in your root directory of the project.
31
+
32
+ If you want to create only the config file, you need to execute:
33
+
34
+ $ rake whisperer:config:create
35
+
36
+ ## Usage
37
+
38
+ ### Describing VCR cassettes
39
+
40
+ VCR cassettes are described in `cassette builders`. It is Ruby DSL which repeats structure of VCR cassette:
41
+
42
+ ```ruby
43
+ Whisperer.define(:arya_stark) do
44
+ request do
45
+ uri 'http://example.com/users/1'
46
+ method :get
47
+ end
48
+
49
+ response do
50
+ status do
51
+ code 200
52
+ message 'OK'
53
+ end
54
+
55
+ headers do
56
+ content_type 'application/json;charset=utf-8'
57
+ end
58
+
59
+ body do
60
+ encoding 'UTF-8'
61
+ factory 'arya_stark', :json
62
+ end
63
+ end
64
+
65
+ recorded_at 'Mon, 13 Jan 2014 21:01:47 GMT'
66
+ end
67
+ ```
68
+
69
+ But, it is Ruby, hence, we can benefit from that. Whisperer uses [FactoryGirl](/thoughtbot/factory_girl) to describe a response body. If you are not familar with FactoryGirl, please, make sure, you know how to use it bofore going on. There are a few ways how factories can be used.
70
+
71
+ You can use one single factory:
72
+
73
+ ```ruby
74
+ body do
75
+ factory 'arya_stark' # we provide only name of the factory
76
+ end
77
+ ```
78
+
79
+ `arya_stark` factory is taken to generate the response body:
80
+
81
+ ```
82
+ string: '{"first_name":"Arya","last_name":"Stark","group":"member"}'
83
+ ```
84
+
85
+ You can use multiple factories to generate collection for your response:
86
+
87
+ ```ruby
88
+ body do
89
+ factories ['robb_stark', 'ned_stark'] # again we provide only names of factories
90
+ end
91
+ ```
92
+
93
+ `robb_stark` and `ned_stark` are taken to generate the response body:
94
+
95
+ ```
96
+ string: '[{"first_name":"Robb","last_name":"Stark","group":"member"},{"first_name":"Ned","last_name":"Stark","group":"member"}]'
97
+ ```
98
+
99
+ You can pass factory objects instead of their names:
100
+
101
+ ```ruby
102
+ body do
103
+ factories = (1..20).to_a.map do |i|
104
+ factories << FactoryGirl.build(
105
+ :article,
106
+ id: 'testid' + i,
107
+ title: 'test name' + i,
108
+ body: 'desc' + i
109
+ )
110
+ end
111
+
112
+ raw_data factories, :json_multiple
113
+ end
114
+ ```
115
+
116
+ It is very useful, when you need generate dynamically instances of a factory.
117
+
118
+ #### Inheritance in cassette builders
119
+
120
+ If you need to generate almost the same VCR cassette, but with a bit differ data, you can do it via inheritance:
121
+
122
+ ```ruby
123
+ Whisperer.define(:robb_stark, parent: :arya_stark) do
124
+ response do
125
+ body do
126
+ factory :robb_stark
127
+ end
128
+ end
129
+ end
130
+ ```
131
+
132
+ In this case all data is taken from `aray_stark` cassette builder, only the response body is different.
133
+
134
+ You can redefine any option of VCR cassette:
135
+
136
+ ```ruby
137
+ Whisperer.define(:robb_stark, parent: :arya_stark) do
138
+ request do
139
+ uri 'http://example.com/users/10'
140
+ end
141
+ end
142
+ ```
143
+
144
+ #### Request/Response Headers
145
+
146
+ While describing headers for a request or response you can use any kind of headers, they are dynamically created:
147
+
148
+ ```ruby
149
+ headers do
150
+ content_length 100
151
+ content_type 'application/json'
152
+ x_requested_with 'XMLHttpRequest'
153
+ end
154
+ ```
155
+
156
+ In a cassette it will look like:
157
+
158
+ ```
159
+ Content-Length:
160
+ - '100'
161
+ Content-Type:
162
+ - application/json
163
+ X-Requested-With
164
+ - XMLHttpRequest
165
+ ```
166
+
167
+ #### Placeholder for FactoryGirl
168
+
169
+ Since VCR is used to stub interractions with external services, there is a big chance that you don't have Ruby model to be used for defining factories. In most cases, you don't need them to generate VCR cassettes. Whisperer offers the placeholder class:
170
+
171
+ ```ruby
172
+ FactoryGirl.define do
173
+ factory :arya_stark, class: Placeholder do
174
+ first_name 'Arya'
175
+ last_name 'Stark'
176
+ group 'member'
177
+ end
178
+ end
179
+ ```
180
+
181
+ Placeholder is a simple class inheriting `OpenStruct` class:
182
+
183
+ ```ruby
184
+ Placeholder = Class.new(OpenStruct)
185
+ ```
186
+
187
+ It decouples factories from your application.
188
+
189
+ ### Serializers for a response body
190
+
191
+ When an external API is subbed with VCR, API response has some format like Json, XML or any other formats. Whisperer supports possibility to convert factories into a format your external API uses. Such mechanism is provided by **serializers** which are used along with building a response body. Whisperer has only 2 serializers:
192
+
193
+ - json
194
+ - multiple json
195
+
196
+ `Json` serializer is used for serializing one single factory:
197
+
198
+ ```ruby
199
+ response do
200
+ body do
201
+ factory :robb_stark
202
+ serializer :json
203
+ end
204
+ end
205
+ ```
206
+
207
+ The purpose of `json `serializer is to convert a given factory into Json format.
208
+
209
+ `Multiple Json` serializer is used for serializing a collection of factories:
210
+
211
+ ```ruby
212
+ body do
213
+ factories ['robb_stark', 'ned_stark']
214
+ serializer :json_multiple
215
+ end
216
+ ```
217
+
218
+ It is very similar to `Json` serializer, but in this case it goes through the array, builds factories, serializes a received array of objects.
219
+
220
+ If you need to define your own serializer, it is very easy to do. At first you need to define your own serializer class inhering `Whisperer::Serializes::Base` class:
221
+
222
+ ```ruby
223
+ class MySerializer < Whisperer::Serializers::Base
224
+ def serialize
225
+ do_something_with(@obj)
226
+ end
227
+ end
228
+ ```
229
+
230
+ *Note:* `@obj` is an `OpenStruct` instance in this example.
231
+
232
+ Then you need to register the new serializer:
233
+
234
+ ```ruby
235
+ Whisperer.register_serializer(:my_serializer, Serializers::MySerializer)
236
+ ```
237
+
238
+ Now, it can be used as any other serializer:
239
+
240
+ ```ruby
241
+ response do
242
+ body do
243
+ factory :robb_stark
244
+ serializer :my_serializer
245
+ end
246
+ end
247
+ ```
248
+
249
+ ### Configuration
250
+
251
+ You can configure Whisperer through `.whisperer.yml` which should be created in a root directory of your project. It gives you following options:
252
+
253
+ - generate_to - the path to save generated cassettes
254
+ - builders_matcher - the pattern to find builders
255
+ - factories_matcher - the pattern to find factories
256
+
257
+ Example of such file:
258
+
259
+ ```
260
+ generate_to: 'spec/cassettes/vcr_cassettes/'
261
+ builders_matcher: './spec/cassette_builders/**/*.rb'
262
+ factories_matcher: './spec/factories/*.rb'
263
+ ```
264
+
265
+ ### Generating cassettes
266
+
267
+ To generate cassettes based on cassette builders, you need to launch command:
268
+
269
+ $ rake whisperer:cassettes:generate_all
270
+
271
+ This command will generate new cassettes and re-generate all existing cassettes for VCR.
272
+
273
+ To generate only on particular cassette, you can use this command
274
+
275
+ $ rake whisperer:cassettes:generate[cassette_builder]
276
+
277
+ `cassette_builder` is a name of the cassette builder.
278
+
279
+ ### Generating a sample for the cassette builder
280
+
281
+ Manual creation of cassette builders is painful. There is a command which can help you with that:
282
+
283
+ $ rake whisperer:cassettes:builders:sample
284
+
285
+ It creates a sample for you in the directory with cassette builders, you need to edit it only.
286
+
287
+ ## Contributing
288
+
289
+ 1. Fork it
290
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
291
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
292
+ 4. Push to the branch (`git push origin my-new-feature`)
293
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO.md ADDED
@@ -0,0 +1,47 @@
1
+ ## Release 0.0.1
2
+
3
+ 1. Add info to doc:
4
+ - subpath for generating cassettes
5
+ - factories for requests
6
+
7
+ ## Release 0.0.2
8
+
9
+ 1. Think about the better way for inheriting serializes, now it looks like:
10
+
11
+ ```ruby
12
+ class Whisperer::Serializers::Base
13
+ # ...
14
+ end
15
+
16
+ class Whisperer::Serializers::Json < Whisperer::Serializers::Base
17
+ # ...
18
+ end
19
+
20
+ class Whisperer::Serializers::JsonMultiple < Whisperer::Serializers::Json
21
+ # ...
22
+ end
23
+ ```
24
+
25
+ If an user wants to inherit `Whisperer::Serializers::JsonMultiple` it will look even more crazy.
26
+
27
+ 2. In most cases if we have a serializer for one single factory, we need a serializer for multiple factories. We need to write code which will create a multiple serializer automatically.
28
+ 3. The Whisperer::Config.load method is too complex.
29
+ 4. Create rake task for generating factories based on Vcr responses.
30
+ 5. Try to find better way for defining dynamic attributes for headers, it doesn't work when you write:
31
+
32
+ ```ruby
33
+ Whisperer::Record.new(
34
+ response: {
35
+ headers: {
36
+ content_length: 10
37
+ }
38
+ }
39
+ )
40
+ ```
41
+
42
+ 6. Refactore Whisperer::Record#merge_attrs! method, it should be moved to some another class
43
+ 7. Refactore Whisperer.define method, it is another responsibility which should not leave in this module.
44
+ 8. Think about the issue with touching Whisperer::cassette_records, it is not ok
45
+ 9. `Whisperer::generate` and `Whisperer::generate_all` receive cassette records twice. Also, it is needless to check existence of a cassette record if it is passed from `generate_all` to `generate`.
46
+ 10. Serializers must be stored similar to preprocessors (in the own module/class).
47
+ 11. Check whether we can use a real model instead of OpenStruct while describing factories.
data/lib/whisperer.rb ADDED
@@ -0,0 +1,102 @@
1
+ require 'whisperer/version'
2
+
3
+ require 'virtus'
4
+ require 'vcr'
5
+
6
+ require 'whisperer/config'
7
+
8
+ require 'whisperer/placeholder'
9
+ require 'whisperer/dsl'
10
+ require 'whisperer/helpers'
11
+
12
+ require 'whisperer/generator'
13
+
14
+ require 'whisperer/convertors/hash'
15
+ require 'whisperer/convertors/interaction'
16
+
17
+ require 'whisperer/serializers/json'
18
+ require 'whisperer/serializers/json_multiple'
19
+
20
+ require 'whisperer/preprocessors'
21
+ require 'whisperer/preprocessors/content_length'
22
+ require 'whisperer/preprocessors/response_body'
23
+
24
+ module Whisperer
25
+ @cassette_records = ThreadSafe::Hash.new
26
+ @serializers = ThreadSafe::Hash.new
27
+
28
+ class << self
29
+ attr_reader :cassette_records
30
+ attr_reader :serializers
31
+
32
+ def define(name, options = {}, &block)
33
+ dsl = Dsl.build
34
+ dsl.instance_eval &block
35
+ record = dsl.container
36
+
37
+ if options[:parent]
38
+ original_record = cassette_records[options[:parent]]
39
+
40
+ if original_record.nil?
41
+ raise ArgumentError.new("Parent record \"#{options[:parent]}\" is not declared.")
42
+ else
43
+ record.merge!(original_record)
44
+ end
45
+ end
46
+
47
+ cassette_records[name.to_sym] = record
48
+ end
49
+
50
+ # Returns true if at least one factory is defined, otherwise returns false.
51
+ def defined_any?
52
+ cassette_records.size > 0
53
+ end
54
+
55
+ def generate(name)
56
+ name = name.to_sym
57
+
58
+ unless cassette_records[name]
59
+ raise NocassetteRecordError.new("There is not cassette builder with \"#{name}\" name.")
60
+ end
61
+
62
+ container = cassette_records[name]
63
+
64
+ Generator.generate(container, name)
65
+ end
66
+
67
+ def generate_all
68
+ if defined_any?
69
+ cassette_records.each do |name, container|
70
+ generate(name)
71
+ end
72
+ else
73
+ raise NocassetteRecordError.new('cassette builders are not found.')
74
+ end
75
+ end
76
+
77
+ def serializer(name)
78
+ unless serializers[name]
79
+ raise ArgumentError.new("There is not serializer registered with \"#{name}\" name")
80
+ end
81
+
82
+ serializers[name]
83
+ end
84
+
85
+ def register_serializer(name, class_name)
86
+ serializers[name] = class_name
87
+ end
88
+
89
+ def register_preprocessor(name, class_name)
90
+ Preprocessors.register(name, class_name)
91
+ end
92
+ end
93
+
94
+ class NocassetteRecordError < ArgumentError; end
95
+ end
96
+
97
+
98
+ Whisperer.register_serializer(:json, Whisperer::Serializers::Json)
99
+ Whisperer.register_serializer(:json_multiple, Whisperer::Serializers::JsonMultiple)
100
+
101
+ Whisperer::register_preprocessor(:response_body, Whisperer::Preprocessors::ResponseBody)
102
+ Whisperer::register_preprocessor(:content_length, Whisperer::Preprocessors::ContentLength)