soaspec 0.2.33 → 0.3.1

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 (86) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +15 -15
  3. data/.gitlab-ci.yml +62 -62
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +2 -2
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/ChangeLog +643 -632
  8. data/Dockerfile +7 -7
  9. data/Gemfile +8 -8
  10. data/LICENSE.txt +21 -21
  11. data/README.md +253 -231
  12. data/Rakefile +52 -52
  13. data/Todo.md +16 -16
  14. data/exe/soaspec +140 -138
  15. data/exe/xml_to_yaml_file +43 -43
  16. data/lib/soaspec.rb +118 -106
  17. data/lib/soaspec/baseline.rb +82 -22
  18. data/lib/soaspec/core_ext/hash.rb +44 -44
  19. data/lib/soaspec/cucumber/generic_steps.rb +94 -94
  20. data/lib/soaspec/demo.rb +6 -6
  21. data/lib/soaspec/errors.rb +24 -24
  22. data/lib/soaspec/exchange/exchange.rb +131 -129
  23. data/lib/soaspec/exchange/exchange_extractor.rb +105 -90
  24. data/lib/soaspec/exchange/exchange_properties.rb +28 -28
  25. data/lib/soaspec/exchange/exchange_repeater.rb +21 -21
  26. data/lib/soaspec/exchange/request_builder.rb +108 -70
  27. data/lib/soaspec/exchange/variable_storer.rb +24 -24
  28. data/lib/soaspec/exchange_handlers/exchange_handler.rb +98 -98
  29. data/lib/soaspec/exchange_handlers/exchange_handler_defaults.rb +61 -61
  30. data/lib/soaspec/exchange_handlers/handler_accessors.rb +132 -132
  31. data/lib/soaspec/exchange_handlers/request/rest_request.rb +77 -59
  32. data/lib/soaspec/exchange_handlers/request/soap_request.rb +41 -41
  33. data/lib/soaspec/exchange_handlers/response_extractor.rb +84 -84
  34. data/lib/soaspec/exchange_handlers/rest_exchanger_factory.rb +111 -111
  35. data/lib/soaspec/exchange_handlers/rest_handler.rb +307 -307
  36. data/lib/soaspec/exchange_handlers/rest_methods.rb +65 -65
  37. data/lib/soaspec/exchange_handlers/rest_parameters.rb +112 -112
  38. data/lib/soaspec/exchange_handlers/rest_parameters_defaults.rb +42 -42
  39. data/lib/soaspec/exchange_handlers/soap_handler.rb +241 -241
  40. data/lib/soaspec/exe_helpers.rb +94 -94
  41. data/lib/soaspec/generate_server.rb +48 -48
  42. data/lib/soaspec/generator/.rspec.erb +5 -5
  43. data/lib/soaspec/generator/.travis.yml.erb +5 -5
  44. data/lib/soaspec/generator/Gemfile.erb +8 -8
  45. data/lib/soaspec/generator/README.md.erb +29 -29
  46. data/lib/soaspec/generator/Rakefile.erb +20 -19
  47. data/lib/soaspec/generator/config/data/default.yml.erb +2 -2
  48. data/lib/soaspec/generator/css/bootstrap.css +6833 -6833
  49. data/lib/soaspec/generator/features/support/env.rb.erb +3 -3
  50. data/lib/soaspec/generator/generate_exchange.html.erb +47 -47
  51. data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
  52. data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
  53. data/lib/soaspec/generator/lib/new_rest_service.rb.erb +56 -56
  54. data/lib/soaspec/generator/lib/new_soap_service.rb.erb +29 -29
  55. data/lib/soaspec/generator/lib/package_service.rb.erb +2 -2
  56. data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
  57. data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
  58. data/lib/soaspec/generator/spec/rest_spec.rb.erb +9 -9
  59. data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
  60. data/lib/soaspec/generator/spec/spec_helper.rb.erb +23 -23
  61. data/lib/soaspec/generator/template/soap_template.xml +6 -6
  62. data/lib/soaspec/indifferent_hash.rb +9 -9
  63. data/lib/soaspec/interpreter.rb +70 -70
  64. data/lib/soaspec/matchers.rb +136 -140
  65. data/lib/soaspec/o_auth2.rb +142 -142
  66. data/lib/soaspec/soaspec_shared_examples.rb +26 -26
  67. data/lib/soaspec/spec_logger.rb +143 -143
  68. data/lib/soaspec/template_reader.rb +30 -30
  69. data/lib/soaspec/test_server/bank.wsdl +90 -90
  70. data/lib/soaspec/test_server/get_bank.rb +166 -166
  71. data/lib/soaspec/test_server/id_manager.rb +41 -41
  72. data/lib/soaspec/test_server/invoices.rb +29 -29
  73. data/lib/soaspec/test_server/namespace.xml +14 -14
  74. data/lib/soaspec/test_server/note.xml +5 -5
  75. data/lib/soaspec/test_server/puppy_service.rb +21 -21
  76. data/lib/soaspec/test_server/test_attribute.rb +14 -14
  77. data/lib/soaspec/test_server/test_namespace.rb +14 -14
  78. data/lib/soaspec/version.rb +6 -6
  79. data/lib/soaspec/virtual_server.rb +193 -190
  80. data/lib/soaspec/wait.rb +43 -43
  81. data/lib/soaspec/wsdl_generator.rb +215 -215
  82. data/soaspec.gemspec +58 -58
  83. data/test.wsdl +116 -116
  84. data/test.xml +10 -10
  85. data/test_wsdl.rb +43 -43
  86. metadata +3 -3
@@ -1,6 +1,6 @@
1
- # frozen_string_literal: true
2
-
3
- # Load this file for demoing Soaspec (e.g in IRB). Has common settings applicable for demoing
4
-
5
- require_relative '../soaspec'
6
- Soaspec::SpecLogger.output_to_terminal = true
1
+ # frozen_string_literal: true
2
+
3
+ # Load this file for demoing Soaspec (e.g in IRB). Has common settings applicable for demoing
4
+
5
+ require_relative '../soaspec'
6
+ Soaspec::SpecLogger.output_to_terminal = true
@@ -1,24 +1,24 @@
1
- # frozen_string_literal: true
2
-
3
- module Soaspec
4
- # Standard Error related to Soaspec
5
- class Error < StandardError; end
6
- # Error related to a response
7
- class ResponseError < Error; end
8
- # Error related to not having a recorded baseline yet
9
- class BaselineError < Error; end
10
- end
11
-
12
- # Raised to represent when there's no element at an Xpath
13
- class NoElementAtPath < Soaspec::Error
14
- def initialize(msg = 'No element at path found')
15
- super
16
- end
17
- end
18
-
19
- # Did not find any element by provided key in the Hash
20
- class NoElementInHash < Soaspec::Error
21
- def initialize(msg = 'No element in Hash found')
22
- super
23
- end
24
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Soaspec
4
+ # Standard Error related to Soaspec
5
+ class Error < StandardError; end
6
+ # Error related to a response
7
+ class ResponseError < Error; end
8
+ # Error related to not having a recorded baseline yet
9
+ class BaselineError < Error; end
10
+ end
11
+
12
+ # Raised to represent when there's no element at an Xpath
13
+ class NoElementAtPath < Soaspec::Error
14
+ def initialize(msg = 'No element at path found')
15
+ super
16
+ end
17
+ end
18
+
19
+ # Did not find any element by provided key in the Hash
20
+ class NoElementInHash < Soaspec::Error
21
+ def initialize(msg = 'No element in Hash found')
22
+ super
23
+ end
24
+ end
@@ -1,129 +1,131 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative '../../soaspec'
4
- require_relative 'exchange_properties'
5
- require_relative 'exchange_extractor'
6
- require_relative 'request_builder'
7
- require_relative 'exchange_repeater'
8
- require_relative 'variable_storer'
9
-
10
- # This represents a request / response pair
11
- # Essentially, params in the exchange that are set are related to the request
12
- # What is returned is related to the response
13
- #
14
- # It is tied to an ExchangeHandler that needs to be defined either globally before it's created or in 'default_handler_used'
15
- class Exchange
16
- extend Soaspec::ExchangeProperties
17
- include Soaspec::ExchangeExtractor
18
- include Soaspec::RequestBuilder
19
- include Soaspec::ExchangeRepeater
20
- include Soaspec::VariableStorer
21
-
22
- # Instance of ExchangeHandler for which this exchange is made
23
- attr_accessor :exchange_handler
24
- # @return [Integer] How many times to retry for a success
25
- attr_accessor :retry_count
26
- # @return [Integer] Times request was retried before being returned
27
- attr_accessor :times_retried
28
- # @@return [String] Name used for displaying class
29
- attr_accessor :test_name
30
- # Expect Factory to fail upon trying to create
31
- attr_writer :fail_factory
32
- # Parameters to override for default params
33
- attr_accessor :override_parameters
34
-
35
- # Set retry for success variable to true so that request will be retried
36
- # for retry_count until it's true
37
- def retry_for_success
38
- @retry_for_success = true
39
- self
40
- end
41
-
42
- # This is set on an individual Exchange marking it as one that should be retried
43
- # @return [Bool] Whether to keep making request until success code reached
44
- def retry_for_success?
45
- @retry_for_success
46
- end
47
-
48
- # Defined as general rule from ExchangeHandler
49
- # @return [Boolean] Whether exception is an exception that must be retried
50
- def invalid_exception?
51
- !exchange_handler.retry_on_exceptions.find { |e| e == exchange_handler.exception.class }.nil?
52
- end
53
-
54
- # Override this in subclass to tie that subclass to an ExchangeHandler
55
- # @return [Soaspec::ExchangeHandler] Soaspec::ExchangeHandler used by this exchange
56
- def default_handler_used; end
57
-
58
- # Create new Exchange according to parameters set. A response will be made if called
59
- # explicitly with 'response' method or through other methods that use it like 'status_code'
60
- # @param [Symbol, String] name Name shown in RSpec run
61
- # @param [Hash] override_parameters Parameters to override for default params
62
- def initialize(name = self.class.to_s, override_parameters = {})
63
- self.test_name ||= name.to_s
64
- # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
65
- @exchange_handler ||= default_handler_used || Soaspec.api_handler
66
- raise '@exchange_handler not set. Set either with `Soaspec.api_handler = Handler.new` or within the exchange' unless @exchange_handler
67
-
68
- @fail_factory = nil
69
- @override_parameters = override_parameters
70
- @retry_for_success = false
71
- self.retry_count = exchange_handler.retry_exception_limit
72
- exchange_handler.elements.each { |element| methods_for_element(element) }
73
- end
74
-
75
- # @return [Hash] Hash representing what will be sent
76
- def request_parameters
77
- exchange_handler.request_parameters(@override_parameters)
78
- end
79
-
80
- # Make request to handler with parameters defined
81
- # Will retry until success code reached if retry_for_success? is set
82
- # @return [Response] Response from Api handler
83
- def make_request
84
- Soaspec::SpecLogger.info 'Example ' + test_name
85
- request_params = @override_parameters
86
- (0..retry_count).each do |count|
87
- response = exchange_handler.make_request(request_params)
88
- return response if !retry_for_success? && !invalid_exception?
89
- return response if (200..299).cover? exchange_handler.status_code_for(response)
90
-
91
- sleep exchange_handler.retry_pause_time # Time before retrying
92
- self.times_retried = count
93
- break response if count == retry_count
94
- end
95
- end
96
-
97
- # Name describing this class when used with `RSpec.describe`
98
- # This will make the request and store the response
99
- # @return [String] Name given when initializing
100
- def to_s
101
- test_name
102
- end
103
-
104
- # @return [RestClient::Response,Savon::Response] Returns response object from Api.
105
- # Will make the request if not made and then cache it for later on
106
- # @example For SOAP it will be a Savon response
107
- # response.body (body of response as Hash)
108
- # response.header (head of response as Hash)
109
- # @example For REST it will be a RestClient::Response
110
- def response
111
- require 'forwardable'
112
- Soaspec.last_exchange = self
113
- @response ||= make_request
114
- @response.define_singleton_method(:exchange) { Soaspec.last_exchange } unless @response.respond_to?(:exchange)
115
- @response.extend Forwardable
116
- @response.delegate %i[value_from_path values_from_path] => :exchange
117
- @response
118
- end
119
-
120
- # @return [ResponseObject] Currently returning response object. This will change (in 0.3) to be itself to
121
- # allow easy method chaining
122
- def call
123
- if Soaspec.log_warnings
124
- warn 'This "call" method will be changed to return "Exchange" object in 0.3. ' \
125
- 'Use "response" method if you want the "response" object'
126
- end
127
- response
128
- end
129
- end
1
+ # frozen_string_literal: true
2
+
3
+ require_relative '../../soaspec'
4
+ require_relative 'exchange_properties'
5
+ require_relative 'exchange_extractor'
6
+ require_relative 'request_builder'
7
+ require_relative 'exchange_repeater'
8
+ require_relative 'variable_storer'
9
+
10
+ # This represents a request / response pair
11
+ # Essentially, params in the exchange that are set are related to the request
12
+ # What is returned is related to the response
13
+ #
14
+ # It is tied to an ExchangeHandler that needs to be defined either globally before it's created or in 'default_handler_used'
15
+ class Exchange
16
+ extend Soaspec::ExchangeProperties
17
+ include Soaspec::ExchangeExtractor
18
+ include Soaspec::RequestBuilder
19
+ include Soaspec::ExchangeRepeater
20
+ include Soaspec::VariableStorer
21
+
22
+ # @return [ExchangeHandler] Instance of ExchangeHandler for which this exchange is made
23
+ attr_accessor :exchange_handler
24
+ # @return [Integer] How many times to retry for a success
25
+ attr_accessor :retry_count
26
+ # @return [Integer] Times request was retried before being returned
27
+ attr_accessor :times_retried
28
+ # @return [String] Name used for displaying class
29
+ attr_accessor :test_name
30
+ # @return [Boolean] Expect Factory to fail upon trying to create
31
+ attr_writer :fail_factory
32
+ # @return [Hash] Parameters to override for default params defined in ExchangeHandler
33
+ # These are the parameters specific to the Exchange and will override, append to
34
+ # what's defined in the ExchangeHandler
35
+ attr_accessor :override_parameters
36
+
37
+ # Set retry for success variable to true so that request will be retried
38
+ # for retry_count until it's true
39
+ def retry_for_success
40
+ @retry_for_success = true
41
+ self
42
+ end
43
+
44
+ # This is set on an individual Exchange marking it as one that should be retried
45
+ # @return [Bool] Whether to keep making request until success code reached
46
+ def retry_for_success?
47
+ @retry_for_success
48
+ end
49
+
50
+ # Defined as general rule from ExchangeHandler
51
+ # @return [Boolean] Whether exception is an exception that must be retried
52
+ def invalid_exception?
53
+ !exchange_handler.retry_on_exceptions.find { |e| e == exchange_handler.exception.class }.nil?
54
+ end
55
+
56
+ # Override this in subclass to tie that subclass to an ExchangeHandler
57
+ # @return [Soaspec::ExchangeHandler] Soaspec::ExchangeHandler used by this exchange
58
+ def default_handler_used; end
59
+
60
+ # Create new Exchange according to parameters set. A response will be made if called
61
+ # explicitly with 'response' method or through other methods that use it like 'status_code'
62
+ # @param [Symbol, String] name Name shown in RSpec run
63
+ # @param [Hash] override_parameters Parameters to override for default params (set through ExchangeHandler or Exchange class)
64
+ # These are the parameters that would be specific for a test
65
+ def initialize(name = self.class.to_s, override_parameters = {})
66
+ if name.is_a? Hash # Name not provided
67
+ override_parameters = name
68
+ name = nil
69
+ end
70
+ self.test_name ||= name.to_s
71
+ @override_parameters = override_parameters
72
+ # As a last resort this uses the global parameter. The handler should be set straight before an exchange is made to use this
73
+ @exchange_handler ||= default_handler_used || Soaspec.api_handler
74
+ @fail_factory = nil
75
+ @retry_for_success = false
76
+ self.retry_count = exchange_handler.retry_exception_limit
77
+ exchange_handler.elements.each { |element| methods_for_element(element) }
78
+ end
79
+
80
+ # @return [Hash] Hash representing what will be sent
81
+ def request_parameters
82
+ exchange_handler.request_parameters(@override_parameters)
83
+ end
84
+
85
+ # Make request to handler with parameters defined
86
+ # Will retry until success code reached if retry_for_success? is set
87
+ # @return [Response] Response from Api handler
88
+ def make_request
89
+ Soaspec::SpecLogger.info 'Example ' + test_name
90
+ request_params = @override_parameters
91
+ (0..retry_count).each do |count|
92
+ response = exchange_handler.make_request(request_params)
93
+ return response if !retry_for_success? && !invalid_exception?
94
+ return response if (200..299).cover? exchange_handler.status_code_for(response)
95
+
96
+ sleep exchange_handler.retry_pause_time # Time before retrying
97
+ self.times_retried = count
98
+ break response if count == retry_count
99
+ end
100
+ end
101
+
102
+ # Name describing this class when used with `RSpec.describe`
103
+ # This will make the request and store the response
104
+ # @return [String] Name given when initializing
105
+ def to_s
106
+ test_name
107
+ end
108
+
109
+ # @return [RestClient::Response,Savon::Response] Returns response object from Api.
110
+ # Will make the request if not made and then cache it for later on
111
+ # @example For SOAP it will be a Savon response
112
+ # response.body (body of response as Hash)
113
+ # response.header (head of response as Hash)
114
+ # @example For REST it will be a RestClient::Response
115
+ def response
116
+ require 'forwardable'
117
+ Soaspec.last_exchange = self
118
+ @response ||= make_request
119
+ @response.define_singleton_method(:exchange) { Soaspec.last_exchange } unless @response.respond_to?(:exchange)
120
+ @response.extend Forwardable
121
+ @response.delegate %i[value_from_path values_from_path] => :exchange
122
+ @response
123
+ end
124
+
125
+ # @return [ResponseObject] Currently returning response object. This will change (in 0.3) to be itself to
126
+ # allow easy method chaining
127
+ def call
128
+ response
129
+ self
130
+ end
131
+ end
@@ -1,90 +1,105 @@
1
- # frozen_string_literal: true
2
-
3
- module Soaspec
4
- # Methods for extracting aspects of the traffic for a request / response
5
- # in an exchange from the ExchangeHandler that it's tied to
6
- module ExchangeExtractor
7
- # Request of API call. Either intended request or actual request
8
- # @return [Object] Object representing request of API
9
- def request
10
- exchange_handler.request(@response)
11
- end
12
-
13
- # Get status code from api class. This is http response code for Web Api
14
- # @return [Integer] Status code from api class
15
- def status_code
16
- exchange_handler.status_code_for(response)
17
- end
18
-
19
- # @return [Boolean] Whether Api success code is successful
20
- def successful_status_code?
21
- (200..299).cover? status_code
22
- end
23
-
24
- # Extract value from path api class
25
- # @example Extract unique value
26
- # @exchange['unique_value_name']
27
- # @example Extract value via JSON path
28
- # @exchange['$..path.to.element']
29
- # @example Extract value via XPath
30
- # @exchange['//path/to/element']
31
- # @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
32
- # @return [String] Value at path
33
- def [](path)
34
- exchange_handler.value_from_path(response, path.to_s)
35
- end
36
-
37
- alias value_from_path []
38
-
39
- # Using same path syntax as []. Returns true of false depending on whether an element is found
40
- # @return [Boolean] Whether an element exists at the path
41
- def element?(path)
42
- self[path]
43
- true
44
- rescue NoElementAtPath
45
- false
46
- end
47
-
48
- # @example Counting items in a JSON list
49
- # # Say there is JSON response {"notes":[{"title":"note1","note":"A note"},{"title":"note2"}]}
50
- # titles = @exchange.values_at_path('$..title')
51
- # expect(titles.count).to eq 2
52
- # expect(titles.first).to eq 'note1'
53
- # @param [String] path XPath, JSONPath to extract value
54
- # @param [String] attribute Attribute to obtain from XML element
55
- # @return [Array] List of values found at path
56
- def values_from_path(path, attribute: nil)
57
- exchange_handler.values_from_path(response, path, attribute: attribute)
58
- end
59
-
60
- # Return the response equivalent of the response. XML, JSON will be converted to a Hash
61
- # @example Counting items in a JSON list
62
- # # Say there is JSON response {"notes":[{"title":"note1","note":"A note"},{"title":"note2"}]}
63
- # hash = @exchange.to_hash
64
- # expect(hash['notes'].count).to eq 2
65
- # expect(hash['notes'].first['title']).to eq 'note1'
66
- # @return [Hash] Hash representing the response of the API
67
- def to_hash
68
- exchange_handler.to_hash(response)
69
- end
70
-
71
- private
72
-
73
- # Used to define methods on an exchange based on what's defined by the ExchangeHandler's methods
74
- # @param [String] element Element to define methods for
75
- def methods_for_element(element)
76
- element_name = element.to_s.split('__custom_path_').last
77
- define_singleton_method(element_name) do
78
- exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
79
- end
80
- define_singleton_method("#{element_name}?") do
81
- begin
82
- __send__ element_name
83
- true
84
- rescue NoElementAtPath
85
- false
86
- end
87
- end
88
- end
89
- end
90
- end
1
+ # frozen_string_literal: true
2
+
3
+ module Soaspec
4
+ # Methods for extracting aspects of the traffic for a request / response
5
+ # in an exchange from the ExchangeHandler that it's tied to
6
+ module ExchangeExtractor
7
+ # Request of API call. Either intended request or actual request
8
+ # @return [Object] Object representing request of API
9
+ def request
10
+ exchange_handler.request(@response)
11
+ end
12
+
13
+ # Get status code from api class. This is http response code for Web Api
14
+ # @return [Integer] Status code from api class
15
+ def status_code
16
+ exchange_handler.status_code_for(response)
17
+ end
18
+
19
+ # @return [Boolean] Whether Api success code is successful
20
+ def successful_status_code?
21
+ (200..299).cover? status_code
22
+ end
23
+
24
+ # Extract value from path api class
25
+ # @example Extract unique value
26
+ # @exchange['unique_value_name']
27
+ # @example Extract value via JSON path
28
+ # @exchange['$..path.to.element']
29
+ # @example Extract value via XPath
30
+ # @exchange['//path/to/element']
31
+ # @param [Object] path Path to return element for api class E.g - for SOAP this is XPath string. For JSON, this is Hash dig Array
32
+ # @return [String] Value at path
33
+ def [](path)
34
+ exchange_handler.value_from_path(response, path.to_s)
35
+ end
36
+
37
+ alias value_from_path []
38
+
39
+ # @return [Symbol] Type of response. XML, JSON, etc
40
+ def format
41
+ Interpreter.response_type_for(response)
42
+ end
43
+
44
+ # Using same path syntax as []. Returns true of false depending on whether an element is found
45
+ # @return [Boolean] Whether an element exists at the path
46
+ def element?(path)
47
+ self[path]
48
+ true
49
+ rescue NoElementAtPath
50
+ false
51
+ end
52
+
53
+ # @example Counting items in a JSON list
54
+ # # Say there is JSON response {"notes":[{"title":"note1","note":"A note"},{"title":"note2"}]}
55
+ # titles = @exchange.values_at_path('$..title')
56
+ # expect(titles.count).to eq 2
57
+ # expect(titles.first).to eq 'note1'
58
+ # @param [String] path XPath, JSONPath to extract value
59
+ # @param [String] attribute Attribute to obtain from XML element
60
+ # @return [Array] List of values found at path
61
+ def values_from_path(path, attribute: nil)
62
+ exchange_handler.values_from_path(response, path, attribute: attribute)
63
+ end
64
+
65
+ # Return the response equivalent of the response. XML, JSON will be converted to a Hash
66
+ # @example Counting items in a JSON list
67
+ # # Say there is JSON response {"notes":[{"title":"note1","note":"A note"},{"title":"note2"}]}
68
+ # hash = @exchange.to_hash
69
+ # expect(hash['notes'].count).to eq 2
70
+ # expect(hash['notes'].first['title']).to eq 'note1'
71
+ # @return [Hash] Hash representing the response of the API
72
+ def to_hash
73
+ exchange_handler.to_hash(response)
74
+ end
75
+
76
+ # @return [String] Get multiline pretty version of response
77
+ def pretty_response_body
78
+ case format
79
+ when :json then JSON.pretty_generate to_hash
80
+ when :xml then response.body # TODO: Single line XML make multiline
81
+ else
82
+ response.body
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ # Used to define methods on an exchange based on what's defined by the ExchangeHandler's methods
89
+ # @param [String] element Element to define methods for
90
+ def methods_for_element(element)
91
+ element_name = element.to_s.split('__custom_path_').last
92
+ define_singleton_method(element_name) do
93
+ exchange_handler.__send__(element, response) # Forward the call onto handler to retrieve the element for the response
94
+ end
95
+ define_singleton_method("#{element_name}?") do
96
+ begin
97
+ __send__ element_name
98
+ true
99
+ rescue NoElementAtPath
100
+ false
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end