soaspec 0.1.1 → 0.1.2

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 (57) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +15 -15
  3. data/.gitlab-ci.yml +31 -31
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +2 -2
  6. data/CODE_OF_CONDUCT.md +74 -74
  7. data/ChangeLog +384 -384
  8. data/Gemfile +6 -6
  9. data/LICENSE.txt +21 -21
  10. data/README.md +85 -85
  11. data/Rakefile +24 -24
  12. data/Todo.md +6 -6
  13. data/exe/soaspec +119 -119
  14. data/exe/soaspec-virtual-server +103 -103
  15. data/exe/xml_to_yaml_file +60 -60
  16. data/lib/soaspec.rb +91 -91
  17. data/lib/soaspec/core_ext/hash.rb +83 -83
  18. data/lib/soaspec/exchange.rb +234 -234
  19. data/lib/soaspec/exchange_handlers/exchange_handler.rb +103 -103
  20. data/lib/soaspec/exchange_handlers/handler_accessors.rb +106 -106
  21. data/lib/soaspec/exchange_handlers/rest_accessors.rb +92 -92
  22. data/lib/soaspec/exchange_handlers/rest_handler.rb +311 -311
  23. data/lib/soaspec/exchange_handlers/rest_methods.rb +44 -44
  24. data/lib/soaspec/exchange_handlers/soap_handler.rb +236 -236
  25. data/lib/soaspec/exe_helpers.rb +56 -56
  26. data/lib/soaspec/generator/.rspec.erb +5 -5
  27. data/lib/soaspec/generator/.travis.yml.erb +5 -5
  28. data/lib/soaspec/generator/Gemfile.erb +8 -8
  29. data/lib/soaspec/generator/README.md.erb +29 -29
  30. data/lib/soaspec/generator/Rakefile.erb +19 -19
  31. data/lib/soaspec/generator/config/data/default.yml.erb +1 -1
  32. data/lib/soaspec/generator/lib/blz_service.rb.erb +26 -26
  33. data/lib/soaspec/generator/lib/dynamic_class_content.rb.erb +12 -12
  34. data/lib/soaspec/generator/lib/shared_example.rb.erb +8 -8
  35. data/lib/soaspec/generator/spec/dynamic_soap_spec.rb.erb +12 -12
  36. data/lib/soaspec/generator/spec/soap_spec.rb.erb +51 -51
  37. data/lib/soaspec/generator/spec/spec_helper.rb.erb +20 -20
  38. data/lib/soaspec/generator/template/soap_template.xml +6 -6
  39. data/lib/soaspec/interpreter.rb +40 -40
  40. data/lib/soaspec/matchers.rb +65 -65
  41. data/lib/soaspec/not_found_errors.rb +13 -13
  42. data/lib/soaspec/soaspec_shared_examples.rb +24 -24
  43. data/lib/soaspec/spec_logger.rb +27 -27
  44. data/lib/soaspec/test_server/bank.wsdl +90 -90
  45. data/lib/soaspec/test_server/get_bank.rb +160 -160
  46. data/lib/soaspec/test_server/invoices.rb +27 -27
  47. data/lib/soaspec/test_server/namespace.xml +14 -14
  48. data/lib/soaspec/test_server/note.xml +5 -5
  49. data/lib/soaspec/test_server/puppy_service.rb +20 -20
  50. data/lib/soaspec/test_server/test_attribute.rb +13 -13
  51. data/lib/soaspec/version.rb +2 -2
  52. data/lib/soaspec/wsdl_generator.rb +144 -144
  53. data/soaspec.gemspec +46 -45
  54. data/test.wsdl +116 -116
  55. data/test.xml +10 -10
  56. data/test_wsdl.rb +43 -43
  57. metadata +17 -3
@@ -1,45 +1,45 @@
1
-
2
- module Soaspec
3
- # Contains commonly used REST methods
4
- module RestMethods
5
- # Make REST Post Exchange
6
- # @param [String] name Name of test displayed
7
- # @param [Hash] params Exchange parameters
8
- # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
9
- def post(name, params = {})
10
- Exchange.new(name, method: :post, **params)
11
- end
12
-
13
- # Make REST Patch Exchange
14
- # @param [String] name Name of test displayed
15
- # @param [Hash] params Exchange parameters
16
- # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
17
- def patch(name, params = {})
18
- Exchange.new(name, method: :patch, **params)
19
- end
20
-
21
- # Make REST Put Exchange
22
- # @param [String] name Name of test displayed
23
- # @param [Hash] params Exchange parameters
24
- # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
25
- def put(name, params = {})
26
- Exchange.new(name, method: :put, **params)
27
- end
28
-
29
- # Make REST Get Exchange
30
- # @param [String] name Name of test displayed
31
- # @param [Hash] params Exchange parameters
32
- # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
33
- def get(name, params = {})
34
- Exchange.new(name, method: :get, **params)
35
- end
36
-
37
- # Make REST Delete Exchange
38
- # @param [String] name Name of test displayed
39
- # @param [Hash] params Exchange parameters
40
- # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
41
- def delete(name, params = {})
42
- Exchange.new(name, method: :delete, **params)
43
- end
44
- end
1
+
2
+ module Soaspec
3
+ # Contains commonly used REST methods
4
+ module RestMethods
5
+ # Make REST Post Exchange
6
+ # @param [String] name Name of test displayed
7
+ # @param [Hash] params Exchange parameters
8
+ # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
9
+ def post(name, params = {})
10
+ Exchange.new(name, method: :post, **params)
11
+ end
12
+
13
+ # Make REST Patch Exchange
14
+ # @param [String] name Name of test displayed
15
+ # @param [Hash] params Exchange parameters
16
+ # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
17
+ def patch(name, params = {})
18
+ Exchange.new(name, method: :patch, **params)
19
+ end
20
+
21
+ # Make REST Put Exchange
22
+ # @param [String] name Name of test displayed
23
+ # @param [Hash] params Exchange parameters
24
+ # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
25
+ def put(name, params = {})
26
+ Exchange.new(name, method: :put, **params)
27
+ end
28
+
29
+ # Make REST Get Exchange
30
+ # @param [String] name Name of test displayed
31
+ # @param [Hash] params Exchange parameters
32
+ # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
33
+ def get(name, params = {})
34
+ Exchange.new(name, method: :get, **params)
35
+ end
36
+
37
+ # Make REST Delete Exchange
38
+ # @param [String] name Name of test displayed
39
+ # @param [Hash] params Exchange parameters
40
+ # @return [Exchange] Instance of Exchange class. Assertions are made by default on the response body
41
+ def delete(name, params = {})
42
+ Exchange.new(name, method: :delete, **params)
43
+ end
44
+ end
45
45
  end
@@ -1,237 +1,237 @@
1
-
2
- require_relative 'exchange_handler'
3
- require_relative '../core_ext/hash'
4
- require_relative '../not_found_errors'
5
- require_relative 'handler_accessors'
6
- require_relative '../interpreter'
7
- require 'forwardable'
8
-
9
- module Soaspec
10
-
11
- # Accessors specific to SOAP handler
12
- module SoapAccessors
13
- # Define attributes set on root SOAP element
14
- def root_attributes(attributes)
15
- define_method('request_root_attributes') do
16
- attributes
17
- end
18
- end
19
- end
20
-
21
- # Wraps around Savon client defining default values dependent on the soap request
22
- class SoapHandler < ExchangeHandler
23
- extend Soaspec::SoapAccessors
24
- extend Forwardable
25
-
26
- delegate [:operations] => :client
27
-
28
- # Savon client used to make SOAP calls
29
- attr_accessor :client
30
- # SOAP Operation to use by default
31
- attr_accessor :operation
32
-
33
- # Attributes set at the root XML element of SOAP request
34
- def request_root_attributes
35
- nil
36
- end
37
-
38
- # Options to log xml request and response
39
- def logging_options
40
- {
41
- log: true, # See request and response. (Put this in traffic file)
42
- log_level: :debug,
43
- logger: Soaspec::SpecLogger.create,
44
- pretty_print_xml: true # Prints XML pretty
45
- }
46
- end
47
-
48
- # Default Savon options. See http://savonrb.com/version2/globals.html for details
49
- # @return [Hash] Default Savon options for all BasicSoapHandler
50
- def default_options
51
- {
52
- ssl_verify_mode: :none, # Easier for testing. Not so secure
53
- follow_redirects: true, # Necessary for many API calls
54
- soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
55
- raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
56
- # Things could go wrong if not set properly
57
- # env_namespace: :soap, # Change environment namespace
58
- # namespace_identifier: :tst, # Change namespace element
59
- # element_form_default: :qualified # Populate each element with namespace
60
- # namespace: 'http://Extended_namespace.xsd' change root namespace
61
- # basic_auth: 'user', 'password'
62
- }
63
- end
64
-
65
- # Add values to here when extending this class to have default Savon options.
66
- # See http://savonrb.com/version2/globals.html for details
67
- # @return [Hash] Savon options adding to & overriding defaults
68
- def savon_options
69
- {
70
- }
71
- end
72
-
73
- # Setup object to handle communicating with a particular SOAP WSDL
74
- # @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
75
- def initialize(name = self.class.to_s, options = {})
76
- @default_hash = {}
77
- @request_option = :hash
78
- if name.is_a?(Hash) && options == {} # If name is not set
79
- options = name
80
- name = self.class.to_s
81
- end
82
- super
83
- set_remove_key(options, :operation)
84
- set_remove_key(options, :default_hash)
85
- set_remove_key(options, :template_name)
86
- merged_options = Soaspec.log_api_traffic? ? default_options.merge(logging_options) : default_options
87
- merged_options.merge! savon_options
88
- merged_options.merge!(options)
89
- self.client = Savon.client(merged_options)
90
- end
91
-
92
- # Used in making request via hash or in template via Erb
93
- def request_body_params(request_parameters)
94
- test_values = request_parameters[:body] || request_parameters
95
- test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
96
- end
97
-
98
- # Used in together with Exchange request that passes such override parameters
99
- # @param [Hash] request_parameters Parameters used to overwrite defaults in request
100
- def make_request(request_parameters)
101
- test_values = request_body_params request_parameters
102
- begin
103
- if @request_option == :template
104
- request_body = File.read('template/' + template_name)
105
- render_body = ERB.new(request_body).result(binding)
106
- client.call(operation, xml: render_body) # Call the SOAP operation with the request XML provided
107
- elsif @request_option == :hash
108
- client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
109
- end
110
- rescue Savon::HTTPError => e
111
- e
112
- end
113
- end
114
-
115
- # @param [Hash] format Format of expected result
116
- # @return [Object] Generic body to be displayed in error messages
117
- def response_body(response, format: :hash)
118
- case format
119
- when :hash
120
- response.body
121
- when :raw
122
- response.xml
123
- else
124
- response.body
125
- end
126
- end
127
-
128
- # @return [Boolean] Whether the request found the desired value or not
129
- def found?(response)
130
- status_code_for(response) != 404
131
- end
132
-
133
- # Response status code for response. '200' indicates a success
134
- # @param [Savon::Response] response
135
- # @return [Integer] Status code
136
- def status_code_for(response)
137
- response.http.code
138
- end
139
-
140
- # @return [Boolean] Whether response includes provided string within it
141
- def include_in_body?(response, expected)
142
- response.to_xml.to_s.include? expected
143
- end
144
-
145
- # @param [Symbol] expected
146
- # @return [Boolean] Whether response body contains expected key
147
- def include_key?(response, expected)
148
- body = response.body
149
- body.extend Hashie::Extensions::DeepFind
150
- !body.deep_find_all(expected).empty?
151
- end
152
-
153
- # Convert all XML nodes to lowercase
154
- # @param [Nokogiri::XML::Document]
155
- def convert_to_lower_case(xml_doc)
156
- xml_doc.traverse do |node|
157
- node.name = node.name.downcase if node.kind_of?(Nokogiri::XML::Element)
158
- end
159
- end
160
-
161
- # Returns the value at the provided xpath
162
- # @param [Savon::Response] response
163
- # @param [String] xpath
164
- # @return [Enumerable] Elements found through Xpath
165
- def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
166
- raise ArgumentError('response and xpath must be passed to method') unless response && xpath
167
- xpath = "//*[@#{attribute}]" unless attribute.nil?
168
- xpath = '//' + xpath if xpath[0] != '/'
169
- temp_doc = response.doc.dup
170
- convert_to_lower_case(temp_doc) if convert_to_lower?
171
- if strip_namespaces? && !xpath.include?(':')
172
- temp_doc.remove_namespaces!
173
- temp_doc.xpath(xpath)
174
- else
175
- temp_doc.xpath(xpath, temp_doc.collect_namespaces)
176
- end
177
- end
178
-
179
- # Based on a exchange, return the value at the provided xpath
180
- # If the path does not begin with a '/', a '//' is added to it
181
- # @param [Savon::Response] response
182
- # @param [String] path Xpath
183
- # @param [String] attribute Generic attribute to find. Will override path
184
- # @return [String] Value at Xpath
185
- def value_from_path(response, path, attribute: nil)
186
- results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
187
- raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
188
- return results.first.inner_text if attribute.nil?
189
- results.first.attributes[attribute].inner_text
190
- end
191
-
192
- # @return [Enumerable] List of values returned from path
193
- def values_from_path(response, path, attribute: nil)
194
- xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
195
- end
196
-
197
- # alias elements xpath_elements_for
198
-
199
- # @return [Boolean] Whether any of the keys of the Body Hash include value
200
- def include_value?(response, expected_value)
201
- response.body.include_value?(expected_value)
202
- end
203
-
204
- # Hash of response body
205
- def to_hash(response)
206
- response.body
207
- end
208
-
209
- # Convenience methods for once off usage of a SOAP request
210
- class << self
211
-
212
- # Implement undefined setter with []= for FactoryBot to use without needing to define params to set
213
- # @param [Object] method_name Name of method not defined
214
- # @param [Object] args Arguments passed to method
215
- # @param [Object] block
216
- def method_missing(method_name, *args, &block)
217
- tmp_class = new(method_name)
218
- operations = tmp_class.operations
219
- if operations.include? method_name
220
- tmp_class.operation = method_name
221
- exchange = Exchange.new(method_name, *args)
222
- exchange.exchange_handler = tmp_class
223
- exchange
224
- else
225
- super
226
- end
227
- end
228
-
229
- def respond_to_missing?(method_name, *args)
230
- tmp_class = new(args)
231
- operations = tmp_class.operations
232
- operations.include?(method_name) || super
233
- end
234
- end
235
-
236
- end
1
+
2
+ require_relative 'exchange_handler'
3
+ require_relative '../core_ext/hash'
4
+ require_relative '../not_found_errors'
5
+ require_relative 'handler_accessors'
6
+ require_relative '../interpreter'
7
+ require 'forwardable'
8
+
9
+ module Soaspec
10
+
11
+ # Accessors specific to SOAP handler
12
+ module SoapAccessors
13
+ # Define attributes set on root SOAP element
14
+ def root_attributes(attributes)
15
+ define_method('request_root_attributes') do
16
+ attributes
17
+ end
18
+ end
19
+ end
20
+
21
+ # Wraps around Savon client defining default values dependent on the soap request
22
+ class SoapHandler < ExchangeHandler
23
+ extend Soaspec::SoapAccessors
24
+ extend Forwardable
25
+
26
+ delegate [:operations] => :client
27
+
28
+ # Savon client used to make SOAP calls
29
+ attr_accessor :client
30
+ # SOAP Operation to use by default
31
+ attr_accessor :operation
32
+
33
+ # Attributes set at the root XML element of SOAP request
34
+ def request_root_attributes
35
+ nil
36
+ end
37
+
38
+ # Options to log xml request and response
39
+ def logging_options
40
+ {
41
+ log: true, # See request and response. (Put this in traffic file)
42
+ log_level: :debug,
43
+ logger: Soaspec::SpecLogger.create,
44
+ pretty_print_xml: true # Prints XML pretty
45
+ }
46
+ end
47
+
48
+ # Default Savon options. See http://savonrb.com/version2/globals.html for details
49
+ # @return [Hash] Default Savon options for all BasicSoapHandler
50
+ def default_options
51
+ {
52
+ ssl_verify_mode: :none, # Easier for testing. Not so secure
53
+ follow_redirects: true, # Necessary for many API calls
54
+ soap_version: 2, # use SOAP 1.2. You will get 415 error if this is incorrect
55
+ raise_errors: false # HTTP errors not cause failure as often negative test scenarios expect not 200 response
56
+ # Things could go wrong if not set properly
57
+ # env_namespace: :soap, # Change environment namespace
58
+ # namespace_identifier: :tst, # Change namespace element
59
+ # element_form_default: :qualified # Populate each element with namespace
60
+ # namespace: 'http://Extended_namespace.xsd' change root namespace
61
+ # basic_auth: 'user', 'password'
62
+ }
63
+ end
64
+
65
+ # Add values to here when extending this class to have default Savon options.
66
+ # See http://savonrb.com/version2/globals.html for details
67
+ # @return [Hash] Savon options adding to & overriding defaults
68
+ def savon_options
69
+ {
70
+ }
71
+ end
72
+
73
+ # Setup object to handle communicating with a particular SOAP WSDL
74
+ # @param [Hash] options Options defining SOAP request. WSDL, authentication, see http://savonrb.com/version2/globals.html for list of options
75
+ def initialize(name = self.class.to_s, options = {})
76
+ @default_hash = {}
77
+ @request_option = :hash
78
+ if name.is_a?(Hash) && options == {} # If name is not set
79
+ options = name
80
+ name = self.class.to_s
81
+ end
82
+ super
83
+ set_remove_key(options, :operation)
84
+ set_remove_key(options, :default_hash)
85
+ set_remove_key(options, :template_name)
86
+ merged_options = Soaspec.log_api_traffic? ? default_options.merge(logging_options) : default_options
87
+ merged_options.merge! savon_options
88
+ merged_options.merge!(options)
89
+ self.client = Savon.client(merged_options)
90
+ end
91
+
92
+ # Used in making request via hash or in template via Erb
93
+ def request_body_params(request_parameters)
94
+ test_values = request_parameters[:body] || request_parameters
95
+ test_values.transform_keys_to_symbols if Soaspec.always_use_keys?
96
+ end
97
+
98
+ # Used in together with Exchange request that passes such override parameters
99
+ # @param [Hash] request_parameters Parameters used to overwrite defaults in request
100
+ def make_request(request_parameters)
101
+ test_values = request_body_params request_parameters
102
+ begin
103
+ if @request_option == :template
104
+ request_body = File.read('template/' + template_name)
105
+ render_body = ERB.new(request_body).result(binding)
106
+ client.call(operation, xml: render_body) # Call the SOAP operation with the request XML provided
107
+ elsif @request_option == :hash
108
+ client.call(operation, message: @default_hash.merge(test_values), attributes: request_root_attributes)
109
+ end
110
+ rescue Savon::HTTPError => e
111
+ e
112
+ end
113
+ end
114
+
115
+ # @param [Hash] format Format of expected result
116
+ # @return [Object] Generic body to be displayed in error messages
117
+ def response_body(response, format: :hash)
118
+ case format
119
+ when :hash
120
+ response.body
121
+ when :raw
122
+ response.xml
123
+ else
124
+ response.body
125
+ end
126
+ end
127
+
128
+ # @return [Boolean] Whether the request found the desired value or not
129
+ def found?(response)
130
+ status_code_for(response) != 404
131
+ end
132
+
133
+ # Response status code for response. '200' indicates a success
134
+ # @param [Savon::Response] response
135
+ # @return [Integer] Status code
136
+ def status_code_for(response)
137
+ response.http.code
138
+ end
139
+
140
+ # @return [Boolean] Whether response includes provided string within it
141
+ def include_in_body?(response, expected)
142
+ response.to_xml.to_s.include? expected
143
+ end
144
+
145
+ # @param [Symbol] expected
146
+ # @return [Boolean] Whether response body contains expected key
147
+ def include_key?(response, expected)
148
+ body = response.body
149
+ body.extend Hashie::Extensions::DeepFind
150
+ !body.deep_find_all(expected).empty?
151
+ end
152
+
153
+ # Convert all XML nodes to lowercase
154
+ # @param [Nokogiri::XML::Document]
155
+ def convert_to_lower_case(xml_doc)
156
+ xml_doc.traverse do |node|
157
+ node.name = node.name.downcase if node.kind_of?(Nokogiri::XML::Element)
158
+ end
159
+ end
160
+
161
+ # Returns the value at the provided xpath
162
+ # @param [Savon::Response] response
163
+ # @param [String] xpath
164
+ # @return [Enumerable] Elements found through Xpath
165
+ def xpath_elements_for(response: nil, xpath: nil, attribute: nil)
166
+ raise ArgumentError('response and xpath must be passed to method') unless response && xpath
167
+ xpath = "//*[@#{attribute}]" unless attribute.nil?
168
+ xpath = '//' + xpath if xpath[0] != '/'
169
+ temp_doc = response.doc.dup
170
+ convert_to_lower_case(temp_doc) if convert_to_lower?
171
+ if strip_namespaces? && !xpath.include?(':')
172
+ temp_doc.remove_namespaces!
173
+ temp_doc.xpath(xpath)
174
+ else
175
+ temp_doc.xpath(xpath, temp_doc.collect_namespaces)
176
+ end
177
+ end
178
+
179
+ # Based on a exchange, return the value at the provided xpath
180
+ # If the path does not begin with a '/', a '//' is added to it
181
+ # @param [Savon::Response] response
182
+ # @param [String] path Xpath
183
+ # @param [String] attribute Generic attribute to find. Will override path
184
+ # @return [String] Value at Xpath
185
+ def value_from_path(response, path, attribute: nil)
186
+ results = xpath_elements_for(response: response, xpath: path, attribute: attribute)
187
+ raise NoElementAtPath, "No value at Xpath '#{path}' in XML #{response.doc}" if results.empty?
188
+ return results.first.inner_text if attribute.nil?
189
+ results.first.attributes[attribute].inner_text
190
+ end
191
+
192
+ # @return [Enumerable] List of values returned from path
193
+ def values_from_path(response, path, attribute: nil)
194
+ xpath_elements_for(response: response, xpath: path, attribute: attribute).map(&:inner_text)
195
+ end
196
+
197
+ # alias elements xpath_elements_for
198
+
199
+ # @return [Boolean] Whether any of the keys of the Body Hash include value
200
+ def include_value?(response, expected_value)
201
+ response.body.include_value?(expected_value)
202
+ end
203
+
204
+ # Hash of response body
205
+ def to_hash(response)
206
+ response.body
207
+ end
208
+
209
+ # Convenience methods for once off usage of a SOAP request
210
+ class << self
211
+
212
+ # Implement undefined setter with []= for FactoryBot to use without needing to define params to set
213
+ # @param [Object] method_name Name of method not defined
214
+ # @param [Object] args Arguments passed to method
215
+ # @param [Object] block
216
+ def method_missing(method_name, *args, &block)
217
+ tmp_class = new(method_name)
218
+ operations = tmp_class.operations
219
+ if operations.include? method_name
220
+ tmp_class.operation = method_name
221
+ exchange = Exchange.new(method_name, *args)
222
+ exchange.exchange_handler = tmp_class
223
+ exchange
224
+ else
225
+ super
226
+ end
227
+ end
228
+
229
+ def respond_to_missing?(method_name, *args)
230
+ tmp_class = new(args)
231
+ operations = tmp_class.operations
232
+ operations.include?(method_name) || super
233
+ end
234
+ end
235
+
236
+ end
237
237
  end