watchdocs-rails 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 65f40dd4195dcebaf6c921fe90e58fa47a797ba2
4
+ data.tar.gz: cd195f1205230dd2e01f6852c3fbfd0ee4c3ec37
5
+ SHA512:
6
+ metadata.gz: b6e626a941d6a69c278cf18eae9cf51df81bbe17f3b37c4fbb21c989e41b3538bf8a3f08b064cccb162fc64e96101a8a8b434aeb772563aafb3b16d4a4e2d58e
7
+ data.tar.gz: fc7014ea8d4bdc6a97a418ece8c25e7137eba5853cfe5c49dcc56b647731511fecd1a2d58aee1310841c5e223690281f6f6b84f9c03f4b7fd5201139f7298d16
data/.DS_Store ADDED
Binary file
data/.gitignore ADDED
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in watchdocs-rails.gemspec
4
+ gemspec
data/README.md ADDED
@@ -0,0 +1,152 @@
1
+ # watchdocs-rails
2
+
3
+ [Watchdocs](http://watchdocs.io) Rails JSON API requests watcher.
4
+
5
+ It captures every JSON response, stores request and response details in temporary storage. Provides methods to send recorded calls to [Watchdocs](http://watchdocs.io) which checks them agains the documentation or create docs for new calls.
6
+
7
+ ## Installation
8
+
9
+ Add to your Gemfile. It is recommended to add to `:test, :development` group.
10
+
11
+ ```ruby
12
+ gem 'watchdocs-rails', '~> 0.1.0'
13
+ ```
14
+
15
+ and run
16
+
17
+ ```
18
+ bundle
19
+ ```
20
+
21
+ ## Configuration
22
+
23
+ Create `config/initializers/watchdocs.rb` and configure the gem if you need to change default configuration:
24
+
25
+ ```ruby
26
+ Watchdocs::Rails.configure do |c|
27
+ c.store_class = Watchdocs::Rails::Store::MemoryStore
28
+ c.temp_directory = 'tmp'
29
+ c.sync_url = 'http://demo8792890.mockable.io/requests'
30
+ end
31
+ ```
32
+
33
+ ### store_class
34
+
35
+ This option allows you to specify class that is responsible for storing recordings.
36
+
37
+ You can select from provided options:
38
+
39
+ - `Watchdocs::Rails::Store::MemoryStore` **(default)** - stores recordings in memory
40
+ - `Watchdocs::Rails::Store::JsonFileStore` - stores in temporary json file
41
+
42
+ or you can implement you own store for recordings. Just create module that implements the following methods:
43
+
44
+ ```ruby
45
+ # Params
46
+ # content - is a Ruby Array of Hashes
47
+ def write(content)
48
+ ...
49
+ end
50
+
51
+ # Returns Ruby Array of Hashes
52
+ def read
53
+ ...
54
+ end
55
+
56
+ def delete!
57
+ ...
58
+ end
59
+
60
+ # Returns true if store already initialized
61
+ def exists?
62
+ ...
63
+ end
64
+ ```
65
+
66
+ ### temp_directory
67
+
68
+ **Applicable only when `JsonFileStore` enabled as `store_class`**
69
+ Directory to store temporary file with recordings.
70
+
71
+ ### sync_url
72
+
73
+ URL for syncing with your Watchdocs project.
74
+
75
+ ## Usage
76
+
77
+ ### Tests
78
+
79
+ If you have some requests specs or features specs that call JSON API then add this line to your `config/environments/test.rb`.
80
+
81
+ ```ruby
82
+ config.middleware.insert(0, Watchdocs::Rails::Middleware)
83
+ ```
84
+
85
+ Update/create your spec hooks:
86
+
87
+ #### RSpec
88
+
89
+ In `specs/rails_helper.rb`:
90
+
91
+ ```ruby
92
+ config.before(:suite) do
93
+ ....
94
+ Watchdocs::Rails::Recordings.clear!
95
+ end
96
+
97
+ config.after(:suite) do
98
+ ....
99
+ Watchdocs::Rails::Recordings.send
100
+ end
101
+ ```
102
+
103
+ #### Minitest
104
+
105
+
106
+ ```
107
+ Minitest.after_run do
108
+ Watchdocs::Rails::Recordings.send
109
+ end
110
+ ```
111
+
112
+ ### Development
113
+
114
+ If you don't have any specs yet. You can add the following line to `config/environments/development.rb`.
115
+
116
+ ```ruby
117
+ config.middleware.insert(0, Watchdocs::Rails::Middleware)
118
+ ```
119
+
120
+ Then the only option is to send recordings manually from `rails c` by running `Watchdocs::Rails::Recordings.send`.
121
+
122
+ **IMPORTANT NOTE: You need to select `JsonFileStore` as storage option in that case.**
123
+
124
+
125
+ ## Versioning
126
+
127
+ Semantic versioning (http://semver.org/spec/v2.0.0.html) is used.
128
+
129
+ For a version number MAJOR.MINOR.PATCH, unless MAJOR is 0:
130
+
131
+ 1. MAJOR version is incremented when incompatible API changes are made,
132
+ 2. MINOR version is incremented when functionality is added in a backwards-compatible manner,
133
+ 3. PATCH version is incremented when backwards-compatible bug fixes are made.
134
+
135
+ Major version "zero" (0.y.z) is for initial development. Anything may change at any time.
136
+ The public API should not be considered stable.
137
+ Furthermore, version "double-zero" (0.0.x) is not intended for public use,
138
+ as even minimal functionality is not guaranteed to be implemented yet.
139
+
140
+ ## Dependencies
141
+
142
+ - httparty
143
+ - configurations
144
+ - mini_memory_store
145
+
146
+ ## Contributing
147
+
148
+ 1. Fork it ( https://github.com/<user>/<gem>/fork )
149
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
150
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
151
+ 4. Push to the branch (`git push origin my-new-feature`)
152
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "watchdocs/rails"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/lib/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,45 @@
1
+ require 'httparty'
2
+
3
+ module Watchdocs
4
+ module Rails
5
+ module Bridge
6
+ class WatchdocsApiError < StandardError; end
7
+
8
+ DEFAULT_ERROR = 'Unknown API Error occured.'.freeze
9
+
10
+ class << self
11
+ def send(payload)
12
+ response = HTTParty.post(
13
+ api_url,
14
+ body: payload.to_json,
15
+ headers: { 'Content-Type' => 'application/json' }
16
+ )
17
+ check_response(response)
18
+ end
19
+
20
+ private
21
+
22
+ def check_response(response)
23
+ case response.code.to_s.chars.first
24
+ when '2'
25
+ true
26
+ when '4', '5'
27
+ raise WatchdocsApiError, get_error(response.body)
28
+ else
29
+ raise WatchdocsApiError, DEFAULT_ERROR
30
+ end
31
+ end
32
+
33
+ def get_error(response_body)
34
+ JSON.parse(response_body)['errors'].join(', ')
35
+ rescue
36
+ DEFAULT_ERROR
37
+ end
38
+
39
+ def api_url
40
+ Watchdocs::Rails.configuration.sync_url
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,16 @@
1
+ require 'configurations'
2
+
3
+ module Watchdocs
4
+ module Rails
5
+ include Configurations
6
+ configurable :store_class,
7
+ :temp_directory,
8
+ :sync_url
9
+
10
+ configuration_defaults do |c|
11
+ c.store_class = Watchdocs::Rails::Store::MemoryStore
12
+ c.temp_directory = 'tmp'
13
+ c.sync_url = 'http://demo8792890.mockable.io/requests'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ module Enumerable
2
+ def filter_data
3
+ map do |v|
4
+ if v.is_a?(Enumerable)
5
+ v.filter_data
6
+ else
7
+ v.json_data_type
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,16 @@
1
+ class Hash
2
+ def filter_data
3
+ filtered = map do |k, v|
4
+ if v.is_a?(Enumerable)
5
+ [k, v.filter_data]
6
+ else
7
+ [k, v.json_data_type]
8
+ end
9
+ end
10
+ Hash[filtered]
11
+ end
12
+
13
+ def upcased_keys
14
+ map { |k, _v| k.upcase }
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ class Object
2
+ def json_data_type
3
+ if is_a?(Numeric)
4
+ 'number'
5
+ elsif is_a?(NilClass)
6
+ 'null'
7
+ elsif is_a?(String)
8
+ 'string'
9
+ elsif is_a?(TrueClass) || is_a?(FalseClass)
10
+ 'boolean'
11
+ else
12
+ '*****'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ require 'watchdocs/rails/core_extensions/enumerable'
2
+ require 'watchdocs/rails/core_extensions/hash'
3
+ require 'watchdocs/rails/core_extensions/object'
@@ -0,0 +1,89 @@
1
+ module Watchdocs
2
+ module Rails
3
+ class Middleware
4
+ attr_reader :app, :report
5
+
6
+ def initialize(app)
7
+ @app = app
8
+ @report = {}
9
+ end
10
+
11
+ def call(env)
12
+ app.call(env).tap do |response|
13
+ if json_response?(response)
14
+ catch_request(env)
15
+ catch_response(response)
16
+ match_endpoint_pattern
17
+ record_call
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def json_response?(response)
25
+ headers = response.second
26
+ headers['Content-Type'] && headers['Content-Type'].include?('json')
27
+ end
28
+
29
+ def catch_request(env)
30
+ @report[:request] = {
31
+ method: env['REQUEST_METHOD'],
32
+ url: env['PATH_INFO'],
33
+ query_string_params: CGI.parse(env['QUERY_STRING']),
34
+ body: parse_body(env['rack.input'].read),
35
+ headers: request_headers(env)
36
+ }
37
+ end
38
+
39
+ def catch_response(response)
40
+ status, headers, body = *response
41
+ @report[:response] = {
42
+ status: status,
43
+ headers: headers.to_hash.upcased_keys,
44
+ body: parse_body(body_string(body))
45
+ }
46
+ end
47
+
48
+ def match_endpoint_pattern
49
+ @report[:endpoint] = begin
50
+ Rails.application.routes.router.routes
51
+ .simulator.memos(report[:request][:url])
52
+ .last.path.spec.to_s.sub('(.:format)', '')
53
+ end
54
+ rescue
55
+ @report[:endpoint] = 'No routes match'
56
+ end
57
+
58
+ def record_call
59
+ Watchdocs::Rails::Recordings.record_call(report)
60
+ end
61
+
62
+ def request_headers(env)
63
+ env.keys
64
+ .select { |k| k.start_with? 'HTTP_' }
65
+ .map { |k| format_header(k) }
66
+ .sort
67
+ end
68
+
69
+ def format_header(header)
70
+ header.sub(/^HTTP_/, '')
71
+ .tr('_', '-')
72
+ end
73
+
74
+ def body_string(body)
75
+ return if body.empty?
76
+ body_string = ''
77
+ body.each { |line| body_string += line }
78
+ body_string
79
+ end
80
+
81
+ def parse_body(body)
82
+ return if body.empty?
83
+ JSON.parse(body).filter_data
84
+ rescue JSON::ParserError
85
+ 'Invalid JSON'
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,47 @@
1
+ module Watchdocs
2
+ module Rails
3
+ module Recordings
4
+ class << self
5
+ def record_call(new_call)
6
+ output = if recordings_exists?
7
+ current_recordings << new_call
8
+ else
9
+ [new_call]
10
+ end
11
+ save_recordings(output)
12
+ end
13
+
14
+ def clear!
15
+ clear_recordings
16
+ end
17
+
18
+ def send
19
+ Watchdocs::Rails::Bridge.send(current_recordings) &&
20
+ clear_recordings
21
+ end
22
+
23
+ private
24
+
25
+ def recordings_exists?
26
+ store.exists?
27
+ end
28
+
29
+ def current_recordings
30
+ store.read
31
+ end
32
+
33
+ def save_recordings(content)
34
+ store.write(content)
35
+ end
36
+
37
+ def clear_recordings
38
+ store.delete!
39
+ end
40
+
41
+ def store
42
+ Watchdocs::Rails.configuration.store_class
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,49 @@
1
+ module Watchdocs
2
+ module Rails
3
+ module Store
4
+ module JsonFileStore
5
+ class StorageError < StandardError; end
6
+
7
+ class << self
8
+ def write(content)
9
+ File.write(path_to_file, content.to_json)
10
+ path_to_file
11
+ rescue StandardError => e
12
+ raise StorageError, e
13
+ end
14
+
15
+ def read
16
+ file = File.open(path_to_file, 'r')
17
+ JSON.parse(file.read)
18
+ rescue JSON::ParserError
19
+ []
20
+ rescue StandardError => e
21
+ raise StorageError, e
22
+ ensure
23
+ file.close
24
+ end
25
+
26
+ def delete!
27
+ File.delete(path_to_file)
28
+ rescue StandardError => e
29
+ raise StorageError, e
30
+ end
31
+
32
+ def exists?
33
+ File.exist?(path_to_file)
34
+ end
35
+
36
+ private
37
+
38
+ def path_to_file
39
+ "#{temp_local_path}/reqests.json"
40
+ end
41
+
42
+ def temp_local_path
43
+ Watchdocs::Rails.configuration.temp_directory
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,33 @@
1
+ require 'mini_memory_store'
2
+
3
+ module Watchdocs
4
+ module Rails
5
+ module Store
6
+ module MemoryStore
7
+ class << self
8
+ def write(content)
9
+ store.set(content)
10
+ end
11
+
12
+ def read
13
+ store.get
14
+ end
15
+
16
+ def delete!
17
+ store.clear
18
+ end
19
+
20
+ def exists?
21
+ store.get
22
+ end
23
+
24
+ private
25
+
26
+ def store
27
+ @store ||= MiniMemoryStore.new
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ module Watchdocs
2
+ module Rails
3
+ module Store
4
+ # You can implement you own store for recordings
5
+ # Just create module that implements following methods
6
+
7
+ ## Params
8
+ ## content - is a Ruby Array of Hashes
9
+ # def write(content)
10
+ # ...
11
+ # end
12
+
13
+ ## Returns Ruby Array of Hashes
14
+ # def read
15
+ # ...
16
+ # end
17
+
18
+ # def delete!
19
+ # ...
20
+ # end
21
+
22
+ ## Returns true if store already initialized
23
+ # def exists?
24
+ # ...
25
+ # end
26
+ end
27
+ end
28
+ end
29
+
30
+ require 'watchdocs/rails/store/file_store'
31
+ require 'watchdocs/rails/store/memory_store'
@@ -0,0 +1,5 @@
1
+ module Watchdocs
2
+ module Rails
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ require 'watchdocs/rails/core_extensions'
2
+ require 'watchdocs/rails/store'
3
+ require 'watchdocs/rails/configuration'
4
+ require 'watchdocs/rails/middleware'
5
+ require 'watchdocs/rails/bridge'
6
+ require 'watchdocs/rails/recordings'
7
+
8
+ module Watchdocs
9
+ module Rails
10
+ end
11
+ end
@@ -0,0 +1 @@
1
+ require 'watchdocs/rails'
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'watchdocs/rails/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'watchdocs-rails'
8
+ spec.version = Watchdocs::Rails::VERSION
9
+ spec.authors = ['mazikwyry']
10
+ spec.email = ['a.mazur@exlabs.co.uk']
11
+ spec.licenses = ['MIT']
12
+
13
+ spec.summary = 'Rails Rack-Middleware for capturing JSON requests
14
+ and responses details'
15
+ spec.description = 'It captures every JSON response, stores request and
16
+ response details in temporary storage.
17
+ Provides methods to send recorded calls to Watchdocs
18
+ which checks them agains the documentation or create
19
+ docs for new calls.'
20
+ spec.homepage = 'http://watchdocs.io'
21
+
22
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
23
+ f.match(%r{^(test|spec|features)/})
24
+ end
25
+ spec.bindir = 'exe'
26
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ['lib']
28
+
29
+ spec.add_development_dependency 'bundler', '~> 1.13'
30
+ spec.add_development_dependency 'rake', '~> 10.0'
31
+ spec.add_runtime_dependency 'mini_memory_store', '~> 0.1.0'
32
+ spec.add_runtime_dependency 'httparty', '~> 0.14.0'
33
+ spec.add_runtime_dependency 'configurations', '~> 2.2', '>= 2.2.0'
34
+ end
metadata ADDED
@@ -0,0 +1,149 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: watchdocs-rails
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - mazikwyry
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-03-09 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.13'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.13'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mini_memory_store
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 0.1.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 0.1.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: httparty
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 0.14.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 0.14.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: configurations
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.2'
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: 2.2.0
79
+ type: :runtime
80
+ prerelease: false
81
+ version_requirements: !ruby/object:Gem::Requirement
82
+ requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '2.2'
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: 2.2.0
89
+ description: |-
90
+ It captures every JSON response, stores request and
91
+ response details in temporary storage.
92
+ Provides methods to send recorded calls to Watchdocs
93
+ which checks them agains the documentation or create
94
+ docs for new calls.
95
+ email:
96
+ - a.mazur@exlabs.co.uk
97
+ executables: []
98
+ extensions: []
99
+ extra_rdoc_files: []
100
+ files:
101
+ - ".DS_Store"
102
+ - ".gitignore"
103
+ - Gemfile
104
+ - README.md
105
+ - Rakefile
106
+ - bin/console
107
+ - bin/setup
108
+ - lib/.DS_Store
109
+ - lib/watchdocs-rails.rb
110
+ - lib/watchdocs/.DS_Store
111
+ - lib/watchdocs/rails.rb
112
+ - lib/watchdocs/rails/bridge.rb
113
+ - lib/watchdocs/rails/configuration.rb
114
+ - lib/watchdocs/rails/core_extensions.rb
115
+ - lib/watchdocs/rails/core_extensions/enumerable.rb
116
+ - lib/watchdocs/rails/core_extensions/hash.rb
117
+ - lib/watchdocs/rails/core_extensions/object.rb
118
+ - lib/watchdocs/rails/middleware.rb
119
+ - lib/watchdocs/rails/recordings.rb
120
+ - lib/watchdocs/rails/store.rb
121
+ - lib/watchdocs/rails/store/file_store.rb
122
+ - lib/watchdocs/rails/store/memory_store.rb
123
+ - lib/watchdocs/rails/version.rb
124
+ - watchdocs-rails.gemspec
125
+ homepage: http://watchdocs.io
126
+ licenses:
127
+ - MIT
128
+ metadata: {}
129
+ post_install_message:
130
+ rdoc_options: []
131
+ require_paths:
132
+ - lib
133
+ required_ruby_version: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - ">="
136
+ - !ruby/object:Gem::Version
137
+ version: '0'
138
+ required_rubygems_version: !ruby/object:Gem::Requirement
139
+ requirements:
140
+ - - ">="
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 2.4.6
146
+ signing_key:
147
+ specification_version: 4
148
+ summary: Rails Rack-Middleware for capturing JSON requests and responses details
149
+ test_files: []