ruby-deepviz 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: e6999d5f4123bd1008e08680b767d07182a366bc
4
+ data.tar.gz: c1789782ba04e29cff292cbcf62b7829334ffd14
5
+ SHA512:
6
+ metadata.gz: b270f7c5adf1a4d60ee6b16b9ada00d2fb108b6528142610a136ffa3e28fe24cbee0f04b86e5d655325f81113be2553850379f51f09924f12e83ec44fbfd10a8
7
+ data.tar.gz: 2e68c2ebf0d5dd8be2ba88047103b65d244c4aaa50348d3f98f12263ee278bd1b9dbeeda848e0970e9189d846422acd8490a5c0eaf1b1970229fcf6ba53e9bb3
@@ -0,0 +1,274 @@
1
+ require 'deepviz/result'
2
+
3
+ class Intel
4
+
5
+ URL_INTEL_SEARCH = 'https://api.deepviz.com/intel/search'
6
+ URL_INTEL_IP = 'https://api.deepviz.com/intel/network/ip'
7
+ URL_INTEL_DOMAIN = 'https://api.deepviz.com/intel/network/domain'
8
+ URL_INTEL_SEARCH_ADVANCED = 'https://api.deepviz.com/intel/search/advanced'
9
+
10
+ def ip_info(api_key, options={})
11
+ if api_key == nil or api_key == ''
12
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
13
+ end
14
+
15
+ defaults = {
16
+ :ip => nil,
17
+ :history => false,
18
+ :time_delta => nil,
19
+ }
20
+
21
+ options = defaults.merge(options)
22
+
23
+ if (!options['ip'].kind_of?(Array) and (options['time_delta'] == nil or options['time_delta'] == '')) or (options['ip'].kind_of?(Array) and options['time_delta'] != nil and options['time_delta'] != '')
24
+ msg = 'Parameters missing or invalid. You must specify either a list of IPs or time delta'
25
+ return Result.new(status=INPUT_ERROR, msg=msg)
26
+ end
27
+
28
+ if options['history']
29
+ _history = 'true'
30
+ else
31
+ _history = 'false'
32
+ end
33
+
34
+ if options['ip'] != nil and !options['ip'].kind_of?(Array)
35
+ msg = 'You must provide one or more IPs in a list'
36
+ return Result.new(status=INPUT_ERROR, msg=msg)
37
+ else
38
+ body = {
39
+ :ip => options['ip'],
40
+ :history => _history,
41
+ :api_key => api_key,
42
+ }
43
+ end
44
+
45
+ if options['time_delta'] != nil and options['time_delta'] != ''
46
+ body = {
47
+ :time_delta => options['time_delta'],
48
+ :history => _history,
49
+ :api_key => api_key,
50
+ }
51
+ end
52
+
53
+ return do_post(body, URL_INTEL_IP)
54
+ end
55
+
56
+ def domain_info(api_key, options={})
57
+ if api_key == nil or api_key == ''
58
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
59
+ end
60
+
61
+ defaults = {
62
+ :domain => nil,
63
+ :filters => nil,
64
+ :history => false,
65
+ :time_delta => nil,
66
+ }
67
+
68
+ options = defaults.merge(options)
69
+
70
+ if (!options['domain'].kind_of?(Array) and (options['time_delta'] == nil or options['time_delta'] == '')) or (options['domain'].kind_of?(Array) and options['time_delta'] != nil and options['time_delta'] != '')
71
+ msg = 'Parameters missing or invalid. You must specify either a list of domains or time delta'
72
+ return Result.new(status=INPUT_ERROR, msg=msg)
73
+ end
74
+
75
+ if options['history']
76
+ _history = 'true'
77
+ else
78
+ _history = 'false'
79
+ end
80
+
81
+ if options['filters'] != nil and !options['filters'].kind_of?(Array)
82
+ msg = 'You must provide one or more output filters in a list'
83
+ return Result.new(status=INPUT_ERROR, msg=msg)
84
+ end
85
+
86
+ body = {}
87
+
88
+ if options['domain'].kind_of?(Array)
89
+ if options['filters'] != nil
90
+ body = {
91
+ :output_filters => options['filters'],
92
+ :domain => options['domain'],
93
+ :history => _history,
94
+ :api_key => api_key,
95
+ }
96
+ else
97
+ body = {
98
+ :domain => options['domain'],
99
+ :history => _history,
100
+ :api_key => api_key,
101
+ }
102
+ end
103
+ end
104
+
105
+ if options['time_delta'] != nil and options['time_delta'] != ''
106
+ if options['filters'] != nil
107
+ body = {
108
+ :time_delta => options['time_delta'],
109
+ :output_filters => options['filters'],
110
+ :history => _history,
111
+ :api_key => api_key,
112
+ }
113
+ else
114
+ body = {
115
+ :time_delta => options['time_delta'],
116
+ :history => _history,
117
+ :api_key => api_key,
118
+ }
119
+ end
120
+ end
121
+
122
+ return do_post(body, URL_INTEL_DOMAIN)
123
+ end
124
+
125
+ def search(api_key, search_string, options={})
126
+ if api_key == nil or api_key == ''
127
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
128
+ end
129
+
130
+ if search_string == nil or search_string == ''
131
+ return Result.new(status=INPUT_ERROR, msg='String to be searched cannot be null or empty')
132
+ end
133
+
134
+ defaults = {
135
+ :start_offset => nil,
136
+ :elements => nil,
137
+ }
138
+
139
+ options = defaults.merge(options)
140
+
141
+ if options['start_offset'] != nil and options['elements'] != nil and options['start_offset'].is_a? Integer and !options['elements'].is_a? Integer
142
+ result_set = ['start=%d' % options['start_offset'], 'rows=%d' % options['elements']]
143
+
144
+ body = {
145
+ :'result_set' => result_set,
146
+ :'string' => search_string,
147
+ :'api_key' => api_key,
148
+ }
149
+ else
150
+ body = {
151
+ :'string' => search_string,
152
+ :'api_key' => api_key,
153
+ }
154
+ end
155
+
156
+ return do_post(body, URL_INTEL_SEARCH)
157
+ end
158
+
159
+ def advanced_search(api_key, options={})
160
+ if api_key == nil or api_key == ''
161
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
162
+ end
163
+
164
+ defaults = {
165
+ :classification => nil,
166
+ :created_files => nil,
167
+ :never_seen => false,
168
+ :time_delta => nil,
169
+ :result_set => nil,
170
+ :sim_hash => nil,
171
+ :imp_hash => nil,
172
+ :ip_range => nil,
173
+ :strings => nil,
174
+ :country => nil,
175
+ :domain => nil,
176
+ :rules => nil,
177
+ :asn => nil,
178
+ :url => nil,
179
+ :ip => nil,
180
+ }
181
+
182
+ options = defaults.merge(options)
183
+
184
+ body = {
185
+ :api_key => api_key
186
+ }
187
+
188
+ if options['created_files'].kind_of?(Array)
189
+ body[:created_files] = options['created_files']
190
+ end
191
+
192
+ _never_seen = 'false'
193
+ if options[:never_seen] != nil and options[:never_seen]
194
+ _never_seen = 'true'
195
+ end
196
+ body[:never_seen] = _never_seen
197
+
198
+ if options['result_set'].kind_of?(Array)
199
+ body[:result_set] = options['result_set']
200
+ end
201
+
202
+ if options['sim_hash'].kind_of?(Array)
203
+ body[:sim_hash] = options['sim_hash']
204
+ end
205
+
206
+ if options['imp_hash'].kind_of?(Array)
207
+ body[:imp_hash] = options['imp_hash']
208
+ end
209
+
210
+ if options['strings'].kind_of?(Array)
211
+ body[:strings] = options['strings']
212
+ end
213
+
214
+ if options['country'].kind_of?(Array)
215
+ body[:country] = options['country']
216
+ end
217
+
218
+ if options['classification'] != nil
219
+ body[:classification] = options['classification']
220
+ end
221
+
222
+ if options['domain'].kind_of?(Array)
223
+ body[:domain] = options['domain']
224
+ end
225
+
226
+ if options['rules'].kind_of?(Array)
227
+ body[:rules] = options['rules']
228
+ end
229
+
230
+ if options['time_delta'] != nil
231
+ body[:time_delta] = options['time_delta']
232
+ end
233
+
234
+ if options['asn'].kind_of?(Array)
235
+ body[:asn] = options['asn']
236
+ end
237
+
238
+ if options['url'].kind_of?(Array)
239
+ body[:url] = options['url']
240
+ end
241
+
242
+ if options['ip'].kind_of?(Array)
243
+ body[:ip] = options['ip']
244
+ end
245
+
246
+ if options['ip_range'] != nil
247
+ body[:ip_range] = options['ip_range']
248
+ end
249
+
250
+ return do_post(body, URL_INTEL_SEARCH_ADVANCED)
251
+ end
252
+
253
+ def do_post(body, api_uri)
254
+ begin
255
+ response = Unirest.post(api_uri,
256
+ headers:{ 'Content-Type' => 'application/json' },
257
+ parameters:body.to_json)
258
+ rescue Exception
259
+ return Result.new(status=NETWORK_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
260
+ end
261
+
262
+ if response.code == 200
263
+ return Result.new(status=SUCCESS, msg=response.body['data'])
264
+ else
265
+ if response.code >= 500
266
+ return Result.new(status=SERVER_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
267
+ else
268
+ return Result.new(status=CLIENT_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
269
+ end
270
+ end
271
+ end
272
+
273
+ private :do_post
274
+ end
@@ -0,0 +1,27 @@
1
+ SUCCESS = 'DEEPVIZ_STATUS_SUCCESS' # Request successfully submitted
2
+ PROCESSING = 'DEEPVIZ_STATUS_PROCESSING'
3
+ INPUT_ERROR = 'DEEPVIZ_STATUS_INPUT_ERROR'
4
+ SERVER_ERROR = 'DEEPVIZ_STATUS_SERVER_ERROR' # Http 5xx
5
+ CLIENT_ERROR = 'DEEPVIZ_STATUS_CLIENT_ERROR' # Http 4xx
6
+ NETWORK_ERROR = 'DEEPVIZ_STATUS_NETWORK_ERROR' # Cannot contact Deepviz
7
+ INTERNAL_ERROR = 'DEEPVIZ_STATUS_INTERNAL_ERROR'
8
+
9
+
10
+ class Result
11
+ attr_reader :status
12
+ attr_writer :status
13
+ attr_reader :msg
14
+ attr_writer :msg
15
+
16
+ @status = nil
17
+ @msg = nil
18
+
19
+ def initialize(status, msg)
20
+ @status = status
21
+ @msg = msg
22
+ end
23
+
24
+ def to_s
25
+ 'Result(status=%s, msg=%s)' % [@status, @msg]
26
+ end
27
+ end
@@ -0,0 +1,250 @@
1
+ require 'json'
2
+ require 'unirest'
3
+ require 'deepviz/result'
4
+
5
+
6
+ class Sandbox
7
+
8
+ URL_UPLOAD_SAMPLE = 'https://api.deepviz.com/sandbox/submit'
9
+ URL_DOWNLOAD_REPORT = 'https://api.deepviz.com/general/report'
10
+ URL_DOWNLOAD_SAMPLE = 'https://api.deepviz.com/sandbox/sample'
11
+ URL_DOWNLOAD_BULK = 'https://api.deepviz.com/sandbox/sample/bulk/retrieve'
12
+ URL_REQUEST_BULK = 'https://api.deepviz.com/sandbox/sample/bulk/request'
13
+
14
+
15
+ def upload_sample(api_key, path)
16
+ if api_key == nil or api_key == ''
17
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
18
+ end
19
+
20
+ if path == nil or path == ''
21
+ return Result.new(status=INPUT_ERROR, msg='File path cannot be null or empty String')
22
+ else
23
+ if !File.exist?(path)
24
+ return Result.new(status=INPUT_ERROR, msg='File does not exists')
25
+ else
26
+ if File.directory?(path)
27
+ return Result.new(status=INPUT_ERROR, msg='Path is a directory instead of a file')
28
+ else
29
+ if !File.readable?(path)
30
+ return Result.new(status=INPUT_ERROR, msg='Cannot open file "%s"' % File.absolute_path(path))
31
+ end
32
+ end
33
+ end
34
+ end
35
+
36
+ begin
37
+ response = Unirest.post(URL_UPLOAD_SAMPLE,
38
+ headers:{ 'Content-Type' => 'application/json' },
39
+ parameters:{ :api_key => api_key, :source => 'ruby_deepviz', :file => File.new(path, 'rb') })
40
+ rescue Exception
41
+ return Result.new(status=NETWORK_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, body['errmsg']])
42
+ end
43
+
44
+ if response.code == 200
45
+ return Result.new(status=SUCCESS, msg=response.body['data'])
46
+ else
47
+ if response.code >= 500
48
+ return Result.new(status=SERVER_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
49
+ else
50
+ return Result.new(status=CLIENT_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
51
+ end
52
+ end
53
+ end
54
+
55
+
56
+ def upload_folder(api_key, path)
57
+ if api_key == nil or api_key == ''
58
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
59
+ end
60
+
61
+ if path == nil or path == ''
62
+ return Result.new(status=INPUT_ERROR, msg='Folder path cannot be null or empty String')
63
+ else
64
+ if !File.exist?(path)
65
+ return Result.new(status=INPUT_ERROR, msg='Directory does not exists')
66
+ else
67
+ if !File.directory?(path)
68
+ return Result.new(status=INPUT_ERROR, msg='Path is a file instead of a directory')
69
+ end
70
+ end
71
+ end
72
+
73
+ if Dir.entries(path).length <= 2
74
+ return Result.new(status=INPUT_ERROR, msg='Empty folder')
75
+ end
76
+
77
+ Dir.foreach(path) { |x|
78
+ if x != '.' and x != '..'
79
+ file_path = File.join(path, x)
80
+ result = upload_sample(api_key, file_path)
81
+ if result.status != SUCCESS
82
+ result.msg = '"Unable to upload file "%s"' % file_path
83
+ return result
84
+ end
85
+ end
86
+ }
87
+
88
+ return Result.new(status=SUCCESS, msg='Every file in folder has been uploaded')
89
+ end
90
+
91
+
92
+ def download_sample(api_key, md5, path)
93
+ if api_key == nil or api_key == ''
94
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
95
+ end
96
+
97
+ if md5 == nil or md5 == ''
98
+ return Result.new(status=INPUT_ERROR, msg='MD5 cannot be null or empty String')
99
+ end
100
+
101
+ if path == nil or path == ''
102
+ return Result.new(status=INPUT_ERROR, msg='Destination path cannot be null or empty String')
103
+ else
104
+ if File.exist?(path) and !File.directory?(path)
105
+ return Result.new(status=INPUT_ERROR, msg='Invalid destination folder')
106
+ end
107
+ end
108
+
109
+ body = {
110
+ :api_key => api_key,
111
+ :md5 => md5
112
+ }
113
+
114
+ begin
115
+ response = Unirest.post(URL_DOWNLOAD_SAMPLE,
116
+ headers:{ 'Accept' => 'application/json' },
117
+ parameters:body.to_json)
118
+ rescue Exception
119
+ return Result.new(status=NETWORK_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, body['errmsg']])
120
+ end
121
+
122
+ if response.code == 200
123
+ dest_path = File.absolute_path(File.join(path, md5))
124
+ open(dest_path, 'wb') do |file|
125
+ file.write(response.body)
126
+ end
127
+
128
+ return Result.new(status=SUCCESS, msg='Sample downloaded to "%s"' % dest_path)
129
+ else
130
+ if response.code >= 500
131
+ return Result.new(status=SERVER_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
132
+ else
133
+ return Result.new(status=CLIENT_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
134
+ end
135
+ end
136
+ end
137
+
138
+
139
+ def sample_result(api_key, md5)
140
+ return sample_report(api_key, md5, ['classification'])
141
+ end
142
+
143
+
144
+ def sample_report(api_key, md5, filters=nil)
145
+ if api_key == nil or api_key == ''
146
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
147
+ end
148
+
149
+ if md5 == nil or md5 == ''
150
+ return Result.new(status=INPUT_ERROR, msg='MD5 cannot be null or empty String')
151
+ end
152
+
153
+ if filters != nil
154
+ body = {:api_key => api_key, :md5 => md5, :output_filters => filters}
155
+ else
156
+ body = {:api_key => api_key, :md5 => md5}
157
+ end
158
+
159
+ return do_post(body, URL_DOWNLOAD_REPORT)
160
+ end
161
+
162
+
163
+ def bulk_download_request(api_key, md5_list)
164
+ if api_key == nil or api_key == ''
165
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
166
+ end
167
+
168
+ if !md5_list.kind_of?(Array)
169
+ return Result.new(status=INPUT_ERROR, msg='MD5 list empty or invalid')
170
+ end
171
+
172
+ body = {
173
+ :api_key => api_key,
174
+ :hashes => md5_list
175
+ }
176
+
177
+ return do_post(body, URL_REQUEST_BULK)
178
+ end
179
+
180
+
181
+ def bulk_download_retrieve(api_key, id_request, path)
182
+ if api_key == nil or api_key == ''
183
+ return Result.new(status=INPUT_ERROR, msg='API key cannot be null or empty String')
184
+ end
185
+
186
+ if id_request == nil or id_request == ''
187
+ return Result.new(status=INPUT_ERROR, msg='Request ID cannot be null or empty String')
188
+ end
189
+
190
+ if path == nil or path == ''
191
+ return Result.new(status=INPUT_ERROR, msg='Destination path cannot be null or empty String')
192
+ else
193
+ if File.exist?(path) and !File.directory?(path)
194
+ return Result.new(status=INPUT_ERROR, msg='Invalid destination folder')
195
+ end
196
+ end
197
+
198
+ body = {
199
+ :api_key => api_key,
200
+ :id_request => id_request.to_s
201
+ }
202
+
203
+ begin
204
+ response = Unirest.post(URL_DOWNLOAD_BULK,
205
+ headers: { 'Accept' => 'application/json' },
206
+ parameters: body.to_json)
207
+ rescue Exception
208
+ return Result.new(status=NETWORK_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, body['errmsg']])
209
+ end
210
+
211
+ if response.code == 200
212
+ dest_path = File.absolute_path(File.join(path, 'bulk_request_%s.zip' % id_request.to_s))
213
+ open(dest_path, 'wb') do |file|
214
+ file.write(response.body)
215
+ end
216
+
217
+ return Result.new(status=SUCCESS, msg='Archive downloaded to "%s"' % dest_path)
218
+ elsif response.code == 428
219
+ return Result.new(status=PROCESSING, msg='%s - Your request is being processed. Please try again in a few minutes' % response.code)
220
+ else
221
+ if response.code >= 500
222
+ return Result.new(status=SERVER_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
223
+ else
224
+ return Result.new(status=CLIENT_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
225
+ end
226
+ end
227
+ end
228
+
229
+ def do_post(body, api_uri)
230
+ begin
231
+ response = Unirest.post(api_uri,
232
+ headers:{ 'Content-Type' => 'application/json' },
233
+ parameters:body.to_json)
234
+ rescue Exception
235
+ return Result.new(status=NETWORK_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
236
+ end
237
+
238
+ if response.code == 200
239
+ return Result.new(status=SUCCESS, msg=response.body['data'])
240
+ else
241
+ if response.code >= 500
242
+ return Result.new(status=SERVER_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
243
+ else
244
+ return Result.new(status=CLIENT_ERROR, msg='%s - Error while connecting to Deepviz: %s' % [response.code, response.body['errmsg']])
245
+ end
246
+ end
247
+ end
248
+
249
+ private :do_post
250
+ end
@@ -0,0 +1,3 @@
1
+ module Deepviz
2
+ VERSION = '1.0.0'
3
+ end
metadata ADDED
@@ -0,0 +1,89 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby-deepviz
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Saferbytes S.r.l.s.
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-02-16 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.12.a
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 1.12.a
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: unirest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.1.2
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.1.2
55
+ description:
56
+ email: info@deepviz.com
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/deepviz/intel.rb
62
+ - lib/deepviz/result.rb
63
+ - lib/deepviz/sandbox.rb
64
+ - lib/deepviz/version.rb
65
+ homepage: https://www.deepviz.com
66
+ licenses:
67
+ - MIT
68
+ metadata: {}
69
+ post_install_message:
70
+ rdoc_options: []
71
+ require_paths:
72
+ - lib
73
+ required_ruby_version: !ruby/object:Gem::Requirement
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ required_rubygems_version: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ requirements: []
84
+ rubyforge_project:
85
+ rubygems_version: 2.2.2
86
+ signing_key:
87
+ specification_version: 4
88
+ summary: ruby-deepviz is a Ruby wrapper for deepviz.com REST APIs
89
+ test_files: []