mongo 2.0.0.rc → 2.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 (78) hide show
  1. checksums.yaml +4 -4
  2. checksums.yaml.gz.sig +0 -0
  3. data.tar.gz.sig +2 -1
  4. data/README.md +1 -4
  5. data/lib/mongo/client.rb +1 -1
  6. data/lib/mongo/collection.rb +1 -1
  7. data/lib/mongo/collection/view.rb +1 -1
  8. data/lib/mongo/collection/view/writable.rb +26 -10
  9. data/lib/mongo/database.rb +1 -1
  10. data/lib/mongo/grid/file.rb +12 -0
  11. data/lib/mongo/grid/file/chunk.rb +1 -1
  12. data/lib/mongo/grid/file/metadata.rb +13 -0
  13. data/lib/mongo/operation/aggregate.rb +1 -1
  14. data/lib/mongo/operation/write.rb +1 -5
  15. data/lib/mongo/operation/write/bulk.rb +17 -0
  16. data/lib/mongo/operation/write/{bulk_delete.rb → bulk/bulk_delete.rb} +1 -1
  17. data/lib/mongo/operation/{bulk_delete → write/bulk/bulk_delete}/result.rb +3 -0
  18. data/lib/mongo/operation/write/{bulk_insert.rb → bulk/bulk_insert.rb} +1 -1
  19. data/lib/mongo/operation/{bulk_insert → write/bulk/bulk_insert}/result.rb +3 -0
  20. data/lib/mongo/operation/write/{bulk_mergable.rb → bulk/bulk_mergable.rb} +0 -0
  21. data/lib/mongo/operation/write/{bulk_update.rb → bulk/bulk_update.rb} +1 -1
  22. data/lib/mongo/operation/{bulk_update → write/bulk/bulk_update}/result.rb +8 -1
  23. data/lib/mongo/operation/write/{legacy_bulk_mergable.rb → bulk/legacy_bulk_mergable.rb} +0 -0
  24. data/lib/mongo/operation/write/delete.rb +2 -0
  25. data/lib/mongo/operation/write/delete/result.rb +40 -0
  26. data/lib/mongo/operation/write/insert.rb +2 -0
  27. data/lib/mongo/operation/write/insert/result.rb +32 -0
  28. data/lib/mongo/operation/write/update.rb +9 -4
  29. data/lib/mongo/operation/write/update/result.rb +160 -0
  30. data/lib/mongo/server/connectable.rb +11 -0
  31. data/lib/mongo/server/connection.rb +1 -0
  32. data/lib/mongo/server/connection_pool.rb +1 -1
  33. data/lib/mongo/server/connection_pool/queue.rb +1 -1
  34. data/lib/mongo/server/monitor.rb +16 -0
  35. data/lib/mongo/server/monitor/connection.rb +1 -0
  36. data/lib/mongo/socket/ssl.rb +30 -8
  37. data/lib/mongo/version.rb +1 -1
  38. data/lib/mongo/write_concern/acknowledged.rb +1 -1
  39. data/lib/mongo/write_concern/unacknowledged.rb +1 -1
  40. data/spec/certificates/ca.pem +17 -0
  41. data/spec/certificates/client.pem +101 -0
  42. data/spec/certificates/crl.pem +10 -0
  43. data/spec/certificates/crl_client_revoked.pem +12 -0
  44. data/spec/certificates/password_protected.pem +51 -0
  45. data/spec/certificates/server.pem +34 -0
  46. data/spec/mongo/collection/view/writable_spec.rb +175 -0
  47. data/spec/mongo/crud_spec.rb +42 -0
  48. data/spec/mongo/grid/file/metadata_spec.rb +23 -0
  49. data/spec/mongo/grid/file_spec.rb +34 -0
  50. data/spec/mongo/operation/write/delete_spec.rb +8 -0
  51. data/spec/mongo/operation/write/insert_spec.rb +21 -8
  52. data/spec/mongo/operation/write/update_spec.rb +52 -1
  53. data/spec/mongo/server/connection_spec.rb +33 -10
  54. data/spec/mongo/server/monitor_spec.rb +14 -0
  55. data/spec/spec_helper.rb +2 -17
  56. data/spec/support/crud.rb +203 -0
  57. data/spec/support/crud/read.rb +144 -0
  58. data/spec/support/crud/write.rb +214 -0
  59. data/spec/support/crud_tests/read/aggregate.yml +43 -0
  60. data/spec/support/crud_tests/read/count.yml +37 -0
  61. data/spec/support/crud_tests/read/distinct.yml +33 -0
  62. data/spec/support/crud_tests/read/find.yml +50 -0
  63. data/spec/support/crud_tests/write/deleteMany.yml +36 -0
  64. data/spec/support/crud_tests/write/deleteOne.yml +49 -0
  65. data/spec/support/crud_tests/write/findOneAndDelete.yml +54 -0
  66. data/spec/support/crud_tests/write/findOneAndReplace.yml +153 -0
  67. data/spec/support/crud_tests/write/findOneAndUpdate.yml +161 -0
  68. data/spec/support/crud_tests/write/insertMany.yml +24 -0
  69. data/spec/support/crud_tests/write/insertOne.yml +19 -0
  70. data/spec/support/crud_tests/write/replaceOne.yml +96 -0
  71. data/spec/support/crud_tests/write/updateMany.yml +83 -0
  72. data/spec/support/crud_tests/write/updateOne.yml +80 -0
  73. metadata +64 -20
  74. metadata.gz.sig +0 -0
  75. data/spec/mongo_orchestration_spec.rb +0 -70
  76. data/spec/support/mongo_orchestration.rb +0 -61
  77. data/spec/support/mongo_orchestration/requestable.rb +0 -109
  78. data/spec/support/mongo_orchestration/standalone.rb +0 -57
@@ -92,7 +92,7 @@ describe Mongo::Operation::Write::Update do
92
92
  })
93
93
  end
94
94
 
95
- context 'when the update passes' do
95
+ context 'when the update succeeds' do
96
96
 
97
97
  let(:document) do
98
98
  { q: { name: 'test' }, u: { '$set' => { field: 'blah' }}, limit: 1 }
@@ -105,6 +105,18 @@ describe Mongo::Operation::Write::Update do
105
105
  it 'updates the document' do
106
106
  expect(result.written_count).to eq(1)
107
107
  end
108
+
109
+ it 'reports the modified count' do
110
+ expect(result.modified_count).to eq(1)
111
+ end
112
+
113
+ it 'reports the matched count' do
114
+ expect(result.matched_count).to eq(1)
115
+ end
116
+
117
+ it 'reports the upserted id as nil' do
118
+ expect(result.upserted_id).to eq(nil)
119
+ end
108
120
  end
109
121
 
110
122
  context 'when the update fails' do
@@ -145,6 +157,18 @@ describe Mongo::Operation::Write::Update do
145
157
  it 'updates the documents' do
146
158
  expect(result.written_count).to eq(2)
147
159
  end
160
+
161
+ it 'reports the modified count' do
162
+ expect(result.modified_count).to eq(2)
163
+ end
164
+
165
+ it 'reports the matched count' do
166
+ expect(result.matched_count).to eq(2)
167
+ end
168
+
169
+ it 'reports the upserted id as nil' do
170
+ expect(result.upserted_id).to eq(nil)
171
+ end
148
172
  end
149
173
 
150
174
  context 'when an update fails' do
@@ -172,6 +196,33 @@ describe Mongo::Operation::Write::Update do
172
196
  }.to raise_error(Mongo::Error::MaxBSONSize)
173
197
  end
174
198
  end
199
+
200
+ context 'when upsert is true' do
201
+
202
+ let(:document) do
203
+ { q: { field: 'non-existent' }, u: { '$set' => { other: 'blah' }}, upsert: true }
204
+ end
205
+
206
+ let(:result) do
207
+ update.execute(authorized_primary.context)
208
+ end
209
+
210
+ it 'inserts the document' do
211
+ expect(result.written_count).to eq(1)
212
+ end
213
+
214
+ it 'reports the modified count' do
215
+ expect(result.modified_count).to eq(0)
216
+ end
217
+
218
+ it 'reports the matched count' do
219
+ expect(result.matched_count).to eq(0)
220
+ end
221
+
222
+ it 'retruns the upserted id' do
223
+ expect(result.upserted_id).to be_a(BSON::ObjectId)
224
+ end
225
+ end
175
226
  end
176
227
  end
177
228
  end
@@ -154,19 +154,14 @@ describe Mongo::Server::Connection do
154
154
  Mongo::Protocol::Query.new(TEST_DB, TEST_COLL, { 'name' => 'testing' })
155
155
  end
156
156
 
157
- let(:delete) do
158
- Mongo::Protocol::Delete.new(TEST_DB, TEST_COLL, {})
159
- end
160
-
161
157
  context 'when providing a single message' do
162
158
 
163
159
  let(:reply) do
164
160
  connection.dispatch([ insert, query ])
165
161
  end
166
162
 
167
- # @todo: Can remove this once we have more implemented with global hooks.
168
163
  after do
169
- connection.dispatch([ delete ])
164
+ authorized_collection.find.delete_many
170
165
  end
171
166
 
172
167
  it 'it dispatchs the message to the socket' do
@@ -188,9 +183,8 @@ describe Mongo::Server::Connection do
188
183
  connection.dispatch([ insert, command ])
189
184
  end
190
185
 
191
- # @todo: Can remove this once we have more implemented with global hooks.
192
186
  after do
193
- connection.dispatch([ delete ])
187
+ authorized_collection.find.delete_many
194
188
  end
195
189
 
196
190
  it 'it dispatchs the message to the socket' do
@@ -216,6 +210,31 @@ describe Mongo::Server::Connection do
216
210
  expect(connection).to_not be_connected
217
211
  end
218
212
  end
213
+
214
+ context 'when the process is forked' do
215
+
216
+ let(:insert) do
217
+ Mongo::Protocol::Insert.new(TEST_DB, TEST_COLL, documents)
218
+ end
219
+
220
+ before do
221
+ expect(Process).to receive(:pid).at_least(:once).and_return(1)
222
+ end
223
+
224
+ after do
225
+ authorized_collection.find.delete_many
226
+ end
227
+
228
+ it 'disconnects the connection' do
229
+ expect(connection).to receive(:disconnect!).and_call_original
230
+ connection.dispatch([ insert ])
231
+ end
232
+
233
+ it 'sets a new pid' do
234
+ connection.dispatch([ insert ])
235
+ expect(connection.pid).to eq(1)
236
+ end
237
+ end
219
238
  end
220
239
 
221
240
  describe '#initialize' do
@@ -252,12 +271,16 @@ describe Mongo::Server::Connection do
252
271
 
253
272
  context 'when ssl options are provided' do
254
273
 
274
+ let(:ssl_options) do
275
+ { :ssl => true, :ssl_key => 'file', :ssl_key_pass_phrase => 'iamaphrase' }
276
+ end
277
+
255
278
  let(:connection) do
256
- described_class.new(server, :ssl => true)
279
+ described_class.new(server, ssl_options)
257
280
  end
258
281
 
259
282
  it 'sets the ssl options' do
260
- expect(connection.send(:ssl_options)).to eq(:ssl => true)
283
+ expect(connection.send(:ssl_options)).to eq(ssl_options)
261
284
  end
262
285
  end
263
286
 
@@ -12,6 +12,20 @@ describe Mongo::Server::Monitor do
12
12
 
13
13
  describe '#scan!' do
14
14
 
15
+ context 'when calling multiple times in succession' do
16
+
17
+ let(:monitor) do
18
+ described_class.new(address, listeners)
19
+ end
20
+
21
+ it 'throttles the scans to minimum 500ms' do
22
+ start = Time.now
23
+ monitor.scan!
24
+ monitor.scan!
25
+ expect(Time.now - start).to be > 0.5
26
+ end
27
+ end
28
+
15
29
  context 'when the ismaster command succeeds' do
16
30
 
17
31
  let(:monitor) do
@@ -22,10 +22,10 @@ require 'mongo'
22
22
  require 'support/travis'
23
23
  require 'support/matchers'
24
24
  require 'support/authorization'
25
- require 'support/mongo_orchestration'
26
25
  require 'support/server_discovery_and_monitoring'
27
26
  require 'support/server_selection_rtt'
28
27
  require 'support/server_selection'
28
+ require 'support/crud'
29
29
 
30
30
  Mongo::Logger.logger = Logger.new($stdout)
31
31
  Mongo::Logger.logger.level = Logger::INFO
@@ -67,6 +67,7 @@ CURRENT_PATH = File.expand_path(File.dirname(__FILE__))
67
67
  SERVER_DISCOVERY_TESTS = Dir.glob("#{CURRENT_PATH}/support/sdam/**/*.yml")
68
68
  SERVER_SELECTION_RTT_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/rtt/*.yml")
69
69
  SERVER_SELECTION_TESTS = Dir.glob("#{CURRENT_PATH}/support/server_selection/selection/**/*.yml")
70
+ CRUD_TESTS = Dir.glob("#{CURRENT_PATH}/support/crud_tests/**/*.yml")
70
71
 
71
72
  # Determine whether the test clients are connecting to a standlone.
72
73
  #
@@ -128,21 +129,5 @@ def initialize_scanned_client!
128
129
  Mongo::Client.new(ADDRESSES, database: TEST_DB, connect: CONNECT)
129
130
  end
130
131
 
131
- def initialize_mo_standalone!(path = nil)
132
- $mongo_standalone ||= MongoOrchestration.get(:standalone, path: path)
133
- end
134
-
135
- def stop_mo_standalone!
136
- $mongo_standalone.stop if $mongo_standalone
137
- end
138
-
139
- def mongo_orchestration_available?(path = nil)
140
- begin
141
- MongoOrchestration.get(:standalone, path: path)
142
- rescue MongoOrchestration::ServiceNotAvailable
143
- return false
144
- end
145
- end
146
-
147
132
  # require all shared examples
148
133
  Dir['./spec/support/shared/*.rb'].sort.each { |file| require file }
@@ -0,0 +1,203 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ # Matcher for determining if the results of the opeartion match the
16
+ # test's expected results.
17
+ #
18
+ # @since 2.0.0
19
+
20
+ # Matcher for determining if the collection's data matches the
21
+ # test's expected collection data.
22
+ #
23
+ # @since 2.0.0
24
+ RSpec::Matchers.define :match_collection_data do |test|
25
+
26
+ match do |actual|
27
+ test.compare_collection_data
28
+ end
29
+ end
30
+
31
+ require 'support/crud/read'
32
+ require 'support/crud/write'
33
+
34
+ module Mongo
35
+ module CRUD
36
+
37
+ # Represents a CRUD specification test.
38
+ #
39
+ # @since 2.0.0
40
+ class Spec
41
+
42
+ # @return [ String ] description The spec description.
43
+ #
44
+ # @since 2.0.0
45
+ attr_reader :description
46
+
47
+ # Instantiate the new spec.
48
+ #
49
+ # @example Create the spec.
50
+ # Spec.new(file)
51
+ #
52
+ # @param [ String ] file The name of the file.
53
+ #
54
+ # @since 2.0.0
55
+ def initialize(file)
56
+ @spec = YAML.load(ERB.new(File.new(file).read).result)
57
+ @description = File.basename(file)
58
+ @data = @spec['data']
59
+ @crud_tests = @spec['tests']
60
+ end
61
+
62
+ # Get a list of CRUDTests for each test definition.
63
+ #
64
+ # @example Get the list of CRUDTests.
65
+ # spec.tests
66
+ #
67
+ # @return [ Array<CRUDTest> ] The list of CRUDTests.
68
+ #
69
+ # @since 2.0.0
70
+ def tests
71
+ @crud_tests.collect do |test|
72
+ Mongo::CRUD::CRUDTest.new(@data, test)
73
+ end
74
+ end
75
+ end
76
+
77
+ # Represents a single CRUD test.
78
+ #
79
+ # @since 2.0.0
80
+ class CRUDTest
81
+
82
+ # The test description.
83
+ #
84
+ # @return [ String ] description The test description.
85
+ #
86
+ # @since 2.0.0
87
+ attr_reader :description
88
+
89
+ # Instantiate the new CRUDTest.
90
+ #
91
+ # @example Create the test.
92
+ # CRUDTest.new(data, test)
93
+ #
94
+ # @param [ Array<Hash> ] data The documents the collection
95
+ # must have before the test runs.
96
+ # @param [ Hash ] test The test specification.
97
+ #
98
+ # @since 2.0.0
99
+ def initialize(data, test)
100
+ @data = data
101
+ @description = test['description']
102
+ @operation = Operation.get(test['operation'])
103
+ @outcome = test['outcome']
104
+ end
105
+
106
+ # Run the test.
107
+ #
108
+ # @example Run the test.
109
+ # test.run(collection)
110
+ #
111
+ # @param [ Collection ] collection The collection the test
112
+ # should be run on.
113
+ #
114
+ # @return [ Result, Array<Hash> ] The result(s) of running the test.
115
+ #
116
+ # @since 2.0.0
117
+ def run(collection)
118
+ @collection = collection
119
+ @collection.insert_many(@data)
120
+ @operation.execute(collection)
121
+ end
122
+
123
+ # The expected result of running the test.
124
+ #
125
+ # @example Get the expected result of running the test.
126
+ # test.result
127
+ #
128
+ # @return [ Array<Hash> ] The expected result of running the test.
129
+ #
130
+ # @since 2.0.0
131
+ def result
132
+ @operation.has_results? ? @outcome['result'] : []
133
+ end
134
+
135
+ # Compare the existing collection data and the expected collection data.
136
+ #
137
+ # @example Compare the existing and expected collection data.
138
+ # test.compare_collection_data
139
+ #
140
+ # @return [ true, false ] The result of comparing the existing and expected
141
+ # collection data.
142
+ #
143
+ # @since 2.0.0
144
+ def compare_collection_data
145
+ actual_collection_data == outcome_collection_data
146
+ end
147
+
148
+ # Whether this test requires server version >= 2.6 for its results to match
149
+ # the expected results.
150
+ #
151
+ # @example If this test requires >= 2.6.
152
+ # test.requires_2_6?(collection)
153
+ #
154
+ # @param [ true, false ] If write commands are enabled on the server.
155
+ # @param [ Collection ] The collection the test is run on.
156
+ #
157
+ # @return [ true, false ] Whether the test requires server >= 2.6.
158
+ #
159
+ # @since 2.0.0
160
+ def requires_2_6?(write_command_enabled, collection)
161
+ !write_command_enabled && @operation.requires_2_6?(collection)
162
+ end
163
+
164
+ private
165
+
166
+ def outcome_collection_data
167
+ @outcome['collection']['data'] if @outcome['collection']
168
+ end
169
+
170
+ def actual_collection_data
171
+ if @outcome['collection']
172
+ collection_name = @outcome['collection']['name'] || @collection.name
173
+ @collection.database[collection_name].find.to_a
174
+ end
175
+ end
176
+ end
177
+
178
+ # Helper module for instantiating either a Read or Write test operation.
179
+ #
180
+ # @since 2.0.0
181
+ module Operation
182
+ extend self
183
+
184
+ # Get a new Operation.
185
+ #
186
+ # @example Get the operation.
187
+ # Operation.get(spec)
188
+ #
189
+ # @param [ Hash ] spec The operation specification.
190
+ #
191
+ # @return [ Operation::Write, Operation::Read ] The Operation object.
192
+ #
193
+ # @since 2.0.0
194
+ def get(spec)
195
+ if Write::OPERATIONS.keys.include?(spec['name'])
196
+ Write.new(spec)
197
+ else
198
+ Read.new(spec)
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
@@ -0,0 +1,144 @@
1
+ # Copyright (C) 2014-2015 MongoDB, Inc.
2
+ #
3
+ # Licensed under the Apache License, Version 2.0 (the "License");
4
+ # you may not use this file except in compliance with the License.
5
+ # You may obtain a copy of the License at
6
+ #
7
+ # http://www.apache.org/licenses/LICENSE-2.0
8
+ #
9
+ # Unless required by applicable law or agreed to in writing, software
10
+ # distributed under the License is distributed on an "AS IS" BASIS,
11
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ # See the License for the specific language governing permissions and
13
+ # limitations under the License.
14
+
15
+ module Mongo
16
+ module CRUD
17
+ module Operation
18
+
19
+ # Defines common behaviour for running CRUD read operation tests
20
+ # on a collection.
21
+ #
22
+ # @since 2.0.0
23
+ class Read
24
+
25
+ # Map of method names to test operation argument names.
26
+ #
27
+ # @since 2.0.0
28
+ ARGUMENT_MAP = { :sort => 'sort',
29
+ :skip => 'skip',
30
+ :batch_size => 'batch_size',
31
+ :limit => 'limit'
32
+ }
33
+
34
+ # The operation name.
35
+ #
36
+ # @return [ String ] name The operation name.
37
+ #
38
+ # @since 2.0.0
39
+ attr_reader :name
40
+
41
+ # Instantiate the operation.
42
+ #
43
+ # @return [ Hash ] spec The operation spec.
44
+ #
45
+ # @since 2.0.0
46
+ def initialize(spec)
47
+ @spec = spec
48
+ @name = spec['name']
49
+ end
50
+
51
+ # Execute the operation.
52
+ #
53
+ # @example Execute the operation.
54
+ # operation.execute
55
+ #
56
+ # @param [ Collection ] collection The collection to execute the operation on.
57
+ #
58
+ # @return [ Result, Array<Hash> ] The result of executing the operation.
59
+ #
60
+ # @since 2.0.0
61
+ def execute(collection)
62
+ send(name.to_sym, collection)
63
+ end
64
+
65
+ # Whether the operation is expected to have restuls.
66
+ #
67
+ # @example Whether the operation is expected to have results.
68
+ # operation.has_results?
69
+ #
70
+ # @return [ true, false ] If the operation is expected to have results.
71
+ #
72
+ # @since 2.0.0
73
+ def has_results?
74
+ !(name == 'aggregate' &&
75
+ pipeline.find {|op| op.keys.include?('$out') })
76
+ end
77
+
78
+ # Whether the operation requires server version >= 2.6 for its results to
79
+ # match expected results.
80
+ #
81
+ # @example Whether the operation requires >= 2.6 for its results to match.
82
+ # operation.requires_2_6?(collection)
83
+ #
84
+ # @param [ Collection ] collection The collection the operation is executed on.
85
+ #
86
+ # @return [ true, false ] If the operation requires 2.6 for its results to match.
87
+ #
88
+ # @since 2.0.0
89
+ def requires_2_6?(collection)
90
+ name == 'aggregate' && pipeline.find {|op| op.keys.include?('$out') }
91
+ end
92
+
93
+ private
94
+
95
+ def count(collection)
96
+ view = collection.find(filter)
97
+ options = ARGUMENT_MAP.reduce({}) do |opts, (key, value)|
98
+ opts.merge!(key => arguments[value]) if arguments[value]
99
+ opts
100
+ end
101
+ view.count(options)
102
+ end
103
+
104
+ def aggregate(collection)
105
+ collection.find.tap do |view|
106
+ view = view.batch_size(batch_size) if batch_size
107
+ end.aggregate(pipeline).to_a
108
+ end
109
+
110
+ def distinct(collection)
111
+ collection.find(filter).distinct(field_name)
112
+ end
113
+
114
+ def find(collection)
115
+ view = collection.find(filter)
116
+ ARGUMENT_MAP.each do |key, value|
117
+ view = view.send(key, arguments[value]) if arguments[value]
118
+ end
119
+ view.to_a
120
+ end
121
+
122
+ def batch_size
123
+ arguments['batchSize']
124
+ end
125
+
126
+ def filter
127
+ arguments['filter']
128
+ end
129
+
130
+ def pipeline
131
+ arguments['pipeline']
132
+ end
133
+
134
+ def field_name
135
+ arguments['fieldName']
136
+ end
137
+
138
+ def arguments
139
+ @spec['arguments']
140
+ end
141
+ end
142
+ end
143
+ end
144
+ end