tshield 0.8.0.0 → 0.9.0.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.
Files changed (43) hide show
  1. checksums.yaml +5 -5
  2. data/Gemfile +3 -2
  3. data/README.md +147 -4
  4. data/Rakefile +13 -2
  5. data/bin/tshield +5 -5
  6. data/config/tshield.yml +9 -0
  7. data/lib/tshield/after_filter.rb +3 -2
  8. data/lib/tshield/before_filter.rb +3 -2
  9. data/lib/tshield/configuration.rb +57 -36
  10. data/lib/tshield/controller.rb +22 -10
  11. data/lib/tshield/controllers/requests.rb +20 -21
  12. data/lib/tshield/controllers/sessions.rb +2 -3
  13. data/lib/tshield/counter.rb +5 -5
  14. data/lib/tshield/logger.rb +10 -0
  15. data/lib/tshield/options.rb +61 -27
  16. data/lib/tshield/request.rb +25 -28
  17. data/lib/tshield/response.rb +2 -0
  18. data/lib/tshield/server.rb +24 -19
  19. data/lib/tshield/sessions.rb +6 -4
  20. data/lib/tshield/simple_tcp_server.rb +3 -2
  21. data/lib/tshield/version.rb +4 -2
  22. data/lib/tshield.rb +3 -2
  23. data/spec/spec_helper.rb +6 -6
  24. data/spec/tshield/after_filter_spec.rb +7 -0
  25. data/spec/tshield/configuration_spec.rb +57 -20
  26. data/spec/tshield/fixtures/config/tshield.yml +7 -1
  27. data/spec/tshield/fixtures/filters/example_filter.rb +9 -0
  28. data/spec/tshield/request_spec.rb +43 -2
  29. data/tshield.gemspec +28 -22
  30. metadata +139 -67
  31. data/lib/tshield/assets/favicon.ico +0 -0
  32. data/lib/tshield/assets/javascripts/application.js +0 -0
  33. data/lib/tshield/assets/javascripts/bootstrap.min.js +0 -7
  34. data/lib/tshield/assets/javascripts/jquery.min.js +0 -4
  35. data/lib/tshield/assets/stylesheets/application.css +0 -49
  36. data/lib/tshield/assets/stylesheets/bootstrap-theme.min.css +0 -6
  37. data/lib/tshield/assets/stylesheets/bootstrap.min.css +0 -6
  38. data/lib/tshield/controllers/admin/requests.rb +0 -62
  39. data/lib/tshield/controllers/admin/sessions.rb +0 -40
  40. data/lib/tshield/views/admin/requests/index.haml +0 -6
  41. data/lib/tshield/views/admin/requests/show.haml +0 -25
  42. data/lib/tshield/views/admin/sessions/index.haml +0 -6
  43. data/lib/tshield/views/layout/base.haml +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 21eb1813abe621fbbd5239e1b80aa3f0c3985aac
4
- data.tar.gz: 63e502ed3dd6663f90befd4da0a97a91a62b7551
2
+ SHA256:
3
+ metadata.gz: b2bb0faafe574abb4aae439e22b68758271aecc2db875e7075d43a327ee7bc3e
4
+ data.tar.gz: 818926f43b10948fa156028b758c5e9750fc3638c3f8f84c6cf102abe145d292
5
5
  SHA512:
6
- metadata.gz: 7dddf275f556989113f2ab0be6fdc1a3ef431e2e223a5042eb734fcccf016d3a2c5d5bcd5d22e2f1eca70133f7f0aec5351d854eed681dd36e9d3c0f2d971496
7
- data.tar.gz: f1795cd4c6da1c31904a45831d887d82b27affae09fca7794d7e2484e5f62b8133b495e098d6acc70f344cead810aa279d55dcb3a711a67cc507005a6bd8bc50
6
+ metadata.gz: 5fe988d83f69aa0d0053e928fc78c326ee7f08d08a39a6aea3aba4321289efe9e09c91be1f2c5d75e3e61c3202b5651256962c4ad7a66f001e39480e1f6dce92
7
+ data.tar.gz: 658b408f3fb7cd82d67278ac2c8569bd5b2801671630a72a6ca074089ea62ae28cd5f896c9994ff6ad02ee6dec00f3de2590819bee060587d8726efd511b2b63
data/Gemfile CHANGED
@@ -1,4 +1,5 @@
1
- source "https://rubygems.org"
1
+ # frozen_string_literal: true
2
2
 
3
- gemspec
3
+ source 'https://rubygems.org'
4
4
 
5
+ gemspec
data/README.md CHANGED
@@ -1,17 +1,33 @@
1
1
  TShield
2
2
  =======
3
3
 
4
- ## Install
4
+ [![Build Status](https://travis-ci.org/diegorubin/tshield.svg)](https://travis-ci.org/diegorubin/tshield)
5
+ [![SourceLevel](https://app.sourcelevel.io/github/diegorubin/tshield.svg)](https://app.sourcelevel.io/github/diegorubin/tshield)[![Join the chat at https://gitter.im/diegorubin/tshield](https://badges.gitter.im/diegorubin/tshield.svg)](https://gitter.im/diegorubin/tshield?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
6
+
7
+ ## API mocks for development and testing
8
+ TShield is an open source proxy for mocks API responses.
9
+
10
+ * REST
11
+ * SOAP
12
+ * Session manager to separate multiple scenarios (success, error, sucess variation, ...)
13
+ * Lightweight
14
+ * MIT license
15
+
16
+ ## Basic Usage
17
+ ### Install
5
18
 
6
19
  gem install tshield
7
20
 
8
- ## Using
21
+ ### Using
9
22
 
10
23
  To run server execute this command
11
24
 
12
25
  tshield
26
+
27
+ Default port is **4567**
28
+
13
29
 
14
- ### Config example
30
+ #### Config example
15
31
 
16
32
  Before run `tshield` command is necessary to create config file.
17
33
  This is an example of `config/tshield.yml`
@@ -33,7 +49,93 @@ domains:
33
49
  - /users
34
50
  ```
35
51
 
36
- ### Custom controllers
52
+ ## Config options
53
+ ```yaml
54
+ request:
55
+ timeout: 8
56
+ verify_ssl: <<value>>
57
+ domains:
58
+ 'http://my-soap-service:80':
59
+ name: 'my-soap-service'
60
+ headers:
61
+ HTTP_AUTHORIZATION: Authorization
62
+ HTTP_COOKIE: Cookie
63
+ not_save_headers:
64
+ - transfer-encoding
65
+ cache_request: <<value>>
66
+ filters:
67
+ - <<value>>
68
+ excluded_headers:
69
+ - <<value>>
70
+ paths:
71
+ - /Operation
72
+
73
+ 'http://localhost:9090':
74
+ name: 'my-service'
75
+ headers:
76
+ HTTP_AUTHORIZATION: Authorization
77
+ HTTP_COOKIE: Cookie
78
+ HTTP_DOCUMENTID: DocumentId
79
+ not_save_headers:
80
+ - transfer-encoding
81
+ paths:
82
+ - /secure
83
+
84
+ 'http://localhost:9092':
85
+ name: 'my-other-service'
86
+ headers:
87
+ HTTP_AUTHORIZATION: Authorization
88
+ HTTP_COOKIE: Cookie
89
+ not_save_headers:
90
+ - transfer-encoding
91
+ paths:
92
+ - /users
93
+ ```
94
+ **request**
95
+ * **timeout**: wait time for real service in seconds
96
+ * **verify_ssl**: ignores invalid ssl if false
97
+
98
+ **domain**
99
+ * Define Base URI of service
100
+ * **name**: Name to identify the domain in the generated files
101
+ * **headers**: github-issue #17
102
+ * **not_save_headers**: List of headers that should be ignored in generated file
103
+ * **cache_request**: <<some_description>>
104
+ * **filters**: Implementation of before or after filters used in domain requests
105
+ * **excluded_headers**: <<some_description>>
106
+ * **paths**: Paths list of all services that will be called. Used to filter what domain will "receive the request"
107
+
108
+ ## Manage Sessions
109
+
110
+ You can use TShield sessions to separate multiple scenarios for your mocks
111
+
112
+ By default TShield save request/response into
113
+
114
+ requests/<<domain_name>>/<<resource_with_param>>/<<http_verb>>/<<index_based.content and json>>
115
+
116
+ If you start a session a folder with de **session_name** will be placed between **"requests/"** and **"<<domain_name>>"**
117
+
118
+ ### Start TShield session
119
+ **Start new or existing session**
120
+
121
+ _POST_ to http://localhost:4567/sessions?name=<<same_name>>
122
+
123
+ ```
124
+ curl -X POST \
125
+ 'http://localhost:4567/sessions?name=my_valid'
126
+ ```
127
+
128
+ ### Stop TShield session
129
+ **Stop current session**
130
+
131
+ _DELETE_ to http://localhost:4567/sessions
132
+
133
+ ```
134
+ curl -X DELETE \
135
+ http://localhost:4567/sessions
136
+ ```
137
+
138
+ ## Custom controllers
37
139
 
38
140
  All custom controller should be created in `controllers` directory.
39
141
 
@@ -57,4 +159,45 @@ module FooController
57
159
  end
58
160
  ```
59
161
 
162
+ ## Features
163
+
164
+ Description of some tshield features can be found in the features directory.
165
+ This features files are used as base for the component tests.
166
+
167
+ ## Samples
168
+ #### Basic sample for a client app requesting a server API
169
+ [examples/client-api-nodejs](examples/client-api-nodejs)
170
+ #### Basic sample for componente/integration test
171
+ **[WIP]**
172
+
173
+ ## Setup for local development
174
+
175
+ First install dependencies.
176
+ _We recommend use of the RVM to manage project dependencies.__
177
+
178
+ ```
179
+ bundle install
180
+ ```
181
+
182
+ ### Run server to development
183
+
184
+ To start server execute:
185
+
186
+ `rake server`
187
+
188
+ ### Build
189
+
190
+ To generate ruby gem execute:
191
+
192
+ `rake build`
193
+
194
+ ### Test
195
+
196
+ To run all unit tests:
197
+
198
+ `rake spec`
199
+
200
+ To run all component tests:
201
+
202
+ `rake component__tests`
60
203
 
data/Rakefile CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  begin
2
4
  require 'bundler/setup'
3
5
  rescue LoadError
@@ -18,8 +20,17 @@ Bundler::GemHelper.install_tasks
18
20
 
19
21
  require 'rspec/core'
20
22
  require 'rspec/core/rake_task'
21
- desc "Run all specs in spec directory (excluding plugin specs)"
23
+ desc 'Run all specs in spec directory (excluding plugin specs)'
22
24
  RSpec::Core::RakeTask.new
23
25
 
24
- task :default => :spec
26
+ task default: :spec
27
+
28
+ task :component_tests do
29
+ $LOAD_PATH.unshift File.dirname('./lib/tshield.rb')
30
+ exec 'component_tests/run'
31
+ end
25
32
 
33
+ task :server do
34
+ $LOAD_PATH.unshift File.dirname('./lib/tshield.rb')
35
+ exec 'bin/tshield'
36
+ end
data/bin/tshield CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
2
3
 
3
- require 'tshield'
4
-
4
+ require 'tshield/options'
5
5
  TShield::Options.init
6
6
 
7
- tshield = Thread.new {TShield::Server.run!}
7
+ require 'tshield'
8
+ tshield = Thread.new { TShield::Server.run! }
8
9
 
9
10
  configuration = TShield::Configuration.load_configuration
10
11
  (configuration.tcp_servers || []).each do |tcp_server|
11
12
  puts "initializing #{tcp_server['name']}"
12
13
  require "./servers/#{tcp_server['file']}"
13
- klass = Object.const_get(tcp_server['name'])
14
+ klass = Object.const_get(tcp_server['name'])
14
15
  Thread.new { klass.new.listen(tcp_server['port']) }
15
16
  end
16
17
 
17
18
  tshield.join
18
-
@@ -0,0 +1,9 @@
1
+ ---
2
+ request:
3
+ timeout: 8
4
+
5
+ domains:
6
+ 'https://service.com':
7
+ name: 'service'
8
+ paths:
9
+ - /users
@@ -1,11 +1,12 @@
1
- module TShield
1
+ # frozen_string_literal: true
2
2
 
3
+ module TShield
3
4
  # Example:
4
5
  # def filter(response)
5
6
  # response
6
7
  # end
7
8
  class AfterFilter
8
- def filter(response)
9
+ def filter(_response)
9
10
  raise 'should implement method filter and returns response'
10
11
  end
11
12
  end
@@ -1,11 +1,12 @@
1
- module TShield
1
+ # frozen_string_literal: true
2
2
 
3
+ module TShield
3
4
  # Example:
4
5
  # def filter(method, url, options)
5
6
  # [method, url, options]
6
7
  # end
7
8
  class BeforeFilter
8
- def filter(method, url, options)
9
+ def filter(_method, _url, _options)
9
10
  raise 'should implement method filter and returns method, url, options'
10
11
  end
11
12
  end
@@ -1,38 +1,63 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'yaml'
2
4
 
3
5
  require 'tshield/after_filter'
4
6
  require 'tshield/before_filter'
7
+ require 'tshield/options'
8
+ require 'tshield/logger'
5
9
 
6
10
  module TShield
11
+ # Class for read configuration file
7
12
  class Configuration
8
-
9
- attr_accessor :request
10
- attr_accessor :domains
11
- attr_accessor :tcp_servers
12
- attr_writer :session_path
13
+ # Configuration file
14
+ #
15
+ # Possible attributes
16
+ # request:
17
+ # timeout: wait time for real service in seconds
18
+ # verify_ssl: ignores invalid ssl if false
19
+ # domains:
20
+ # 'url':
21
+ # name: Name to identify the domain in the generated files
22
+ # headers: Object to translate received header in tshield to send to
23
+ # original service. Sinatra change keys. Example:
24
+ # HTTP_AUTHORIZATION should be mapped to Authorization
25
+ # (NEED IMPROVEMENT github-issue #https://github.com/diegorubin/tshield/issues/17)
26
+ # not_save_headers: List of headers that should be ignored in generated
27
+ # file
28
+ #
29
+ attr_reader :request
30
+ attr_reader :domains
31
+ attr_reader :tcp_servers
13
32
 
14
33
  def initialize(attributes)
15
- attributes.each do |key, value|
16
- send("#{key}=", value)
17
- end
34
+ attributes.each { |key, value| instance_variable_set("@#{key}", value) }
18
35
 
19
- if File.exists?('filters')
20
- Dir.entries('filters').each do |entry|
21
- next if entry =~ /^\.\.?$/
22
- puts "loading filter #{entry}"
23
- entry.gsub!('.rb', '')
24
- require File.join('.', 'filters', entry)
25
- end
36
+ return unless File.exist?('filters')
37
+
38
+ Dir.entries('filters').each do |entry|
39
+ next if entry =~ /^\.\.?$/
40
+
41
+ TShield.logger.info("loading filter #{entry}")
42
+ entry.gsub!('.rb', '')
43
+
44
+ require File.join('.', 'filters', entry)
26
45
  end
27
46
  end
28
47
 
29
48
  def self.singleton
30
- @@configuration ||= load_configuration
49
+ @singleton ||= load_configuration
50
+ end
51
+
52
+ def self.clear
53
+ @singleton = nil
31
54
  end
32
55
 
33
56
  def get_domain_for(path)
34
57
  domains.each do |url, config|
35
- config['paths'].each { |p| return url if path =~ Regexp.new(p) }
58
+ config['paths'].each do |pattern|
59
+ return url if path =~ Regexp.new(pattern)
60
+ end
36
61
  end
37
62
  nil
38
63
  end
@@ -42,27 +67,26 @@ module TShield
42
67
  end
43
68
 
44
69
  def get_name(domain)
45
- domains[domain]['name'] || domain.gsub(/.*:\/\//, '')
70
+ domains[domain]['name'] || domain.gsub(%r{.*://}, '')
46
71
  end
47
72
 
48
73
  def get_before_filters(domain)
49
74
  get_filters(domain)
50
- .select { |k| k.ancestors.include?(TShield::BeforeFilter) }
75
+ .select { |klass| klass.ancestors.include?(TShield::BeforeFilter) }
51
76
  end
52
77
 
53
78
  def get_after_filters(domain)
54
79
  get_filters(domain)
55
- .select { |k| k.ancestors.include?(TShield::AfterFilter) }
80
+ .select { |klass| klass.ancestors.include?(TShield::AfterFilter) }
56
81
  end
57
82
 
58
83
  def cache_request?(domain)
59
- return true unless domains[domain].include?('cache_request')
60
- domains[domain]['cache_request']
84
+ domains[domain]['cache_request'] || true
61
85
  end
62
86
 
63
87
  def get_filters(domain)
64
88
  (domains[domain]['filters'] || [])
65
- .collect { |f| Class.const_get(f) }
89
+ .collect { |filter| Class.const_get(filter) }
66
90
  end
67
91
 
68
92
  def get_excluded_headers(domain)
@@ -77,22 +101,19 @@ module TShield
77
101
  @session_path || '/sessions'
78
102
  end
79
103
 
80
- def admin_session_path
81
- @admin_session_path || '/admin/sessions'
82
- end
83
-
84
- def admin_request_path
85
- @admin_request_path || '/admin/requests'
104
+ def self.read_configuration_file(config_path)
105
+ configs = YAML.safe_load(File.open(config_path).read)
106
+ Configuration.new(configs)
86
107
  end
87
108
 
88
- private
89
109
  def self.load_configuration
90
- config_path = File.join('config', 'tshield.yml')
91
- file = File.open(config_path)
92
- configs = YAML::load(file.read)
93
- Configuration.new(configs)
110
+ configuration_file = TShield::Options.instance.configuration_file
111
+ read_configuration_file(configuration_file)
112
+ rescue Errno::ENOENT => e
113
+ TShield.logger.fatal(
114
+ "Load configuration file #{configuration_file} failed!\n#{e}"
115
+ )
116
+ raise 'Startup aborted'
94
117
  end
95
-
96
118
  end
97
119
  end
98
-
@@ -1,29 +1,41 @@
1
+ # frozen_string_literal: false
2
+
1
3
  require 'sinatra'
2
4
 
5
+ require 'tshield/logger'
6
+
3
7
  module TShield
8
+ # TShield Controller
4
9
  module Controller
5
-
6
10
  def self.included(base)
7
11
  base.extend ClassMethods
8
12
  end
9
13
 
14
+ # Implementation of actions
10
15
  module ClassMethods
11
16
  def action(class_method, options)
12
- @@actions = {} unless defined? @@actions
13
- @@actions[class_method] = options
17
+ @actions = {} unless defined? @actions
18
+ @actions[class_method] = options
14
19
  end
15
20
 
16
21
  def registered(app)
17
- @@actions.each do |class_method, options|
18
- puts "== registering #{options[:path]} for methods #{options[:methods].join(',')} with action #{class_method}"
19
- options[:methods].each do |method|
20
- app.send(method, options[:path]) { send(class_method, params, request) }
21
- end
22
+ @actions.each do |class_method, options|
23
+ load_action(app, class_method, options)
22
24
  end
23
25
  end
24
26
 
25
- end
27
+ def load_action(app, class_method, options)
28
+ msg = "== registering #{options[:path]}"
29
+ msg << " for methods #{options[:methods].join(',')}"
30
+ msg << " with action #{class_method}"
26
31
 
32
+ TShield.logger.info(msg)
33
+ options[:methods].each do |method|
34
+ app.send(method, options[:path]) do
35
+ send(class_method, params, request)
36
+ end
37
+ end
38
+ end
39
+ end
27
40
  end
28
41
  end
29
-
@@ -1,4 +1,4 @@
1
- # encoding: utf-8
1
+ # frozen_string_literal: true
2
2
 
3
3
  require 'sinatra'
4
4
 
@@ -12,40 +12,40 @@ require 'tshield/sessions'
12
12
  module TShield
13
13
  module Controllers
14
14
  module Requests
15
- PATHP = /([a-zA-Z0-9\/\._-]+)/
15
+ PATHP = %r{([a-zA-Z0-9/\._-]+)}.freeze
16
16
 
17
17
  def self.registered(app)
18
18
  app.configure :production, :development do
19
19
  app.enable :logging
20
20
  end
21
-
22
- app.get (PATHP) do
21
+
22
+ app.get(PATHP) do
23
23
  treat(params, request, response)
24
24
  end
25
25
 
26
- app.post (PATHP) do
26
+ app.post(PATHP) do
27
27
  treat(params, request, response)
28
28
  end
29
29
 
30
- app.put (PATHP) do
30
+ app.put(PATHP) do
31
31
  treat(params, request, response)
32
32
  end
33
33
 
34
- app.patch (PATHP) do
34
+ app.patch(PATHP) do
35
35
  treat(params, request, response)
36
36
  end
37
37
 
38
- app.head (PATHP) do
38
+ app.head(PATHP) do
39
39
  treat(params, request, response)
40
40
  end
41
41
 
42
- app.delete (PATHP) do
42
+ app.delete(PATHP) do
43
43
  treat(params, request, response)
44
44
  end
45
45
  end
46
46
 
47
47
  module Helpers
48
- def treat(params, request, response)
48
+ def treat(params, request, _response)
49
49
  path = params.fetch('captures', [])[0]
50
50
 
51
51
  debugger if TShield::Options.instance.break?(path: path, moment: :before)
@@ -66,12 +66,11 @@ module TShield
66
66
  ip: request.ip
67
67
  }
68
68
 
69
- if ['POST', 'PUT', 'PATCH'].include? method
70
- result = request.body.read.encode('UTF-8', {
71
- :invalid => :replace,
72
- :undef => :replace,
73
- :replace => ''
74
- })
69
+ if %w[POST PUT PATCH].include? method
70
+ result = request.body.read.encode('UTF-8',
71
+ invalid: :replace,
72
+ undef: :replace,
73
+ replace: '')
75
74
  options[:body] = result
76
75
  end
77
76
 
@@ -80,14 +79,15 @@ module TShield
80
79
  api_response = TShield::Request.new(path, options).response
81
80
 
82
81
  logger.info(
83
- "original=#{api_response.original} method=#{method} path=#{path} content-type=#{request_content_type} session=#{current_session_name(request)}")
82
+ "original=#{api_response.original} method=#{method} path=#{path} content-type=#{request_content_type} session=#{current_session_name(request)}"
83
+ )
84
84
 
85
85
  status api_response.status
86
- headers api_response.headers.reject { |k,v| configuration.get_excluded_headers(domain(path)).include?(k) }
86
+ headers api_response.headers.reject { |k, _v| configuration.get_excluded_headers(domain(path)).include?(k) }
87
87
  body api_response.body
88
88
  end
89
89
 
90
- def set_content_type(request_content_type)
90
+ def set_content_type(_request_content_type)
91
91
  content_type :json
92
92
  end
93
93
 
@@ -97,7 +97,7 @@ module TShield
97
97
  end
98
98
 
99
99
  def add_headers(headers, path)
100
- configuration.get_headers(domain(path)).each do |source, destiny|
100
+ configuration.get_headers(domain(path)).each do |source, destiny|
101
101
  headers[destiny] = request.env[source] unless request.env[source].nil?
102
102
  end
103
103
  end
@@ -113,4 +113,3 @@ module TShield
113
113
  end
114
114
  end
115
115
  end
116
-
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'sinatra/base'
2
4
 
3
5
  require 'tshield/configuration'
@@ -7,7 +9,6 @@ module TShield
7
9
  module Controllers
8
10
  module Sessions
9
11
  def self.registered(app)
10
-
11
12
  app.get TShield::Configuration.singleton.session_path do
12
13
  TShield::Sessions.current(request.ip).to_json
13
14
  end
@@ -19,9 +20,7 @@ module TShield
19
20
  app.delete TShield::Configuration.singleton.session_path do
20
21
  TShield::Sessions.stop(request.ip).to_json
21
22
  end
22
-
23
23
  end
24
24
  end
25
25
  end
26
26
  end
27
-
@@ -1,6 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module TShield
4
+ # Increment counter for sessions requests
2
5
  class Counter
3
-
4
6
  def initialize
5
7
  @requests = {}
6
8
  end
@@ -9,7 +11,7 @@ module TShield
9
11
  requests_to_path = @requests.fetch(path, {})
10
12
  requests_to_method = requests_to_path.fetch(method, 0)
11
13
 
12
- requests_to_path[method] = requests_to_method += 1
14
+ requests_to_path[method] = requests_to_method + 1
13
15
  @requests[path] = requests_to_path
14
16
  end
15
17
 
@@ -18,9 +20,7 @@ module TShield
18
20
  end
19
21
 
20
22
  def to_json(options = {})
21
- @requests.to_json
23
+ @requests.to_json(options)
22
24
  end
23
-
24
25
  end
25
26
  end
26
-
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'logger'
4
+
5
+ # Logger instance for application
6
+ module TShield
7
+ def self.logger
8
+ @logger ||= Logger.new(STDOUT)
9
+ end
10
+ end