defensor 0.11.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.
- data/.gitignore +17 -0
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Guardfile +24 -0
- data/LICENSE.txt +22 -0
- data/README.md +33 -0
- data/Rakefile +11 -0
- data/defensor.gemspec +30 -0
- data/lib/defensor/cattr.rb +296 -0
- data/lib/defensor/version.rb +3 -0
- data/lib/defensor.rb +163 -0
- data/spec/models/defensor_spec.rb +178 -0
- data/spec/spec_helper.rb +6 -0
- metadata +189 -0
data/.gitignore
ADDED
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour
|
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# A sample Guardfile
|
2
|
+
# More info at https://github.com/guard/guard#readme
|
3
|
+
|
4
|
+
guard :rspec do
|
5
|
+
watch(%r{^spec/.+_spec\.rb$})
|
6
|
+
watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
|
7
|
+
watch('spec/spec_helper.rb') { "spec" }
|
8
|
+
|
9
|
+
# Rails example
|
10
|
+
watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
|
11
|
+
watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
|
12
|
+
watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
|
13
|
+
watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
|
14
|
+
watch('config/routes.rb') { "spec/routing" }
|
15
|
+
watch('app/controllers/application_controller.rb') { "spec/controllers" }
|
16
|
+
|
17
|
+
# Capybara features specs
|
18
|
+
watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
|
19
|
+
|
20
|
+
# Turnip features and steps
|
21
|
+
watch(%r{^spec/acceptance/(.+)\.feature$})
|
22
|
+
watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
|
23
|
+
end
|
24
|
+
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Oleg Bovykin
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
# Defensor
|
2
|
+
|
3
|
+
Unofficial gem for working with Defensio API 2.0
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'defensor'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Add initializer with api_key
|
16
|
+
|
17
|
+
Defensor.api_key = "YOUR_KEY_HERE"
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Post document for spam chek:
|
22
|
+
|
23
|
+
d = Defensor.new(content: "lalalalalalal", type: :forum, platform: "my_awesome_app")
|
24
|
+
d.post_document
|
25
|
+
=> [200, {"status"=>"success", "message"=>"", "api-version"=>"2.0", "signature"=>"1fd5c9de6a77f28256fba1", "allow"=>true, "spaminess"=>0.05, "classification"=>"legitimate", "profanity-match"=>false}]
|
26
|
+
|
27
|
+
## Contributing
|
28
|
+
|
29
|
+
1. Fork it
|
30
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
31
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
32
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
33
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/defensor.gemspec
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'defensor'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "defensor"
|
8
|
+
spec.version = Defensor::VERSION
|
9
|
+
spec.authors = ["Oleg Bovykin"]
|
10
|
+
spec.email = ["oleg.bovykin@gmail.com"]
|
11
|
+
spec.description = %q{Unofficial Ruby library for Defensio 2.0}
|
12
|
+
spec.summary = %q{Unofficial Ruby library for Defensio 2.0}
|
13
|
+
spec.homepage = ""
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
22
|
+
spec.add_development_dependency "rake"
|
23
|
+
spec.add_development_dependency "mocha"
|
24
|
+
spec.add_development_dependency "rspec"
|
25
|
+
spec.add_development_dependency "guard-rspec"
|
26
|
+
|
27
|
+
spec.add_dependency "json"
|
28
|
+
spec.add_dependency "facets"
|
29
|
+
spec.add_dependency "httparty"
|
30
|
+
end
|
@@ -0,0 +1,296 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
# Creates a class-variable attribute that can
|
4
|
+
# be accessed both on an instance and class level.
|
5
|
+
#
|
6
|
+
# class CARExample
|
7
|
+
# @@a = 10
|
8
|
+
# cattr :a
|
9
|
+
# end
|
10
|
+
#
|
11
|
+
# CARExample.a #=> 10
|
12
|
+
# CARExample.new.a #=> 10
|
13
|
+
#
|
14
|
+
# NOTE: This method is not a common core extension and is not
|
15
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
16
|
+
#
|
17
|
+
# CREDIT: David Heinemeier Hansson
|
18
|
+
#
|
19
|
+
# @uncommon
|
20
|
+
# require 'facets/module/cattr'
|
21
|
+
#
|
22
|
+
def cattr(*syms)
|
23
|
+
writers, readers = syms.flatten.partition{ |a| a.to_s =~ /=$/ }
|
24
|
+
writers = writers.map{ |e| e.to_s.chomp('=').to_sym }
|
25
|
+
##readers.concat( writers ) # writers also get readers
|
26
|
+
|
27
|
+
cattr_reader(*readers)
|
28
|
+
cattr_writer(*writers)
|
29
|
+
|
30
|
+
return readers + writers
|
31
|
+
end
|
32
|
+
|
33
|
+
# Creates a class-variable attr_reader that can
|
34
|
+
# be accessed both on an instance and class level.
|
35
|
+
#
|
36
|
+
# class CARExample
|
37
|
+
# @@a = 10
|
38
|
+
# cattr_reader :a
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# CARExample.a #=> 10
|
42
|
+
# CARExample.new.a #=> 10
|
43
|
+
#
|
44
|
+
# NOTE: This method is not a common core extension and is not
|
45
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
46
|
+
#
|
47
|
+
# CREDIT: David Heinemeier Hansson
|
48
|
+
#
|
49
|
+
# @uncommon
|
50
|
+
# require 'facets/module/cattr'
|
51
|
+
#
|
52
|
+
def cattr_reader(*syms)
|
53
|
+
syms.flatten.each do |sym|
|
54
|
+
module_eval(<<-EOS, __FILE__, __LINE__)
|
55
|
+
unless defined? @@#{sym}
|
56
|
+
@@#{sym} = nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.#{sym}
|
60
|
+
@@#{sym}
|
61
|
+
end
|
62
|
+
|
63
|
+
def #{sym}
|
64
|
+
@@#{sym}
|
65
|
+
end
|
66
|
+
EOS
|
67
|
+
end
|
68
|
+
return syms
|
69
|
+
end
|
70
|
+
|
71
|
+
# Creates a class-variable attr_writer that can
|
72
|
+
# be accessed both on an instance and class level.
|
73
|
+
#
|
74
|
+
# class CAWExample
|
75
|
+
# cattr_writer :a
|
76
|
+
# def self.a
|
77
|
+
# @@a
|
78
|
+
# end
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# CAWExample.a = 10
|
82
|
+
# CAWExample.a #=> 10
|
83
|
+
# CAWExample.new.a = 29
|
84
|
+
# CAWExample.a #=> 29
|
85
|
+
#
|
86
|
+
# NOTE: This method is not a common core extension and is not
|
87
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
88
|
+
#
|
89
|
+
# CREDIT: David Heinemeier Hansson
|
90
|
+
#
|
91
|
+
# @uncommon
|
92
|
+
# require 'facets/module/cattr'
|
93
|
+
#
|
94
|
+
def cattr_writer(*syms)
|
95
|
+
syms.flatten.each do |sym|
|
96
|
+
module_eval(<<-EOS, __FILE__, __LINE__)
|
97
|
+
unless defined? @@#{sym}
|
98
|
+
@@#{sym} = nil
|
99
|
+
end
|
100
|
+
|
101
|
+
def self.#{sym}=(obj)
|
102
|
+
@@#{sym} = obj
|
103
|
+
end
|
104
|
+
|
105
|
+
def #{sym}=(obj)
|
106
|
+
@@#{sym}=(obj)
|
107
|
+
end
|
108
|
+
EOS
|
109
|
+
end
|
110
|
+
return syms
|
111
|
+
end
|
112
|
+
|
113
|
+
# Creates a class-variable attr_accessor that can
|
114
|
+
# be accessed both on an instance and class level.
|
115
|
+
#
|
116
|
+
# class CAAExample
|
117
|
+
# cattr_accessor :a
|
118
|
+
# end
|
119
|
+
#
|
120
|
+
# CAAExample.a = 10
|
121
|
+
# CAAExample.a #=> 10
|
122
|
+
# mc = CAAExample.new
|
123
|
+
# mc.a #=> 10
|
124
|
+
#
|
125
|
+
# NOTE: This method is not a common core extension and is not
|
126
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
127
|
+
#
|
128
|
+
# CREDIT: David Heinemeier Hansson
|
129
|
+
#
|
130
|
+
# @uncommon
|
131
|
+
# require 'facets/module/cattr'
|
132
|
+
#
|
133
|
+
def cattr_accessor(*syms)
|
134
|
+
cattr_reader(*syms) + cattr_writer(*syms)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Creates a class-variable attribute that can
|
138
|
+
# be accessed both on an instance and class level.
|
139
|
+
#
|
140
|
+
# c = Class.new do
|
141
|
+
# mattr :a
|
142
|
+
# def initialize
|
143
|
+
# @@a = 10
|
144
|
+
# end
|
145
|
+
# end
|
146
|
+
#
|
147
|
+
# c.new.a #=> 10
|
148
|
+
# c.a #=> 10
|
149
|
+
#
|
150
|
+
# NOTE: The #mattr methods may not be as useful for modules as the #cattr
|
151
|
+
# methods are for classes, becuase class-level methods are not "inherited"
|
152
|
+
# across the metaclass for included modules.
|
153
|
+
#
|
154
|
+
# NOTE: This methiod is not a common core extension and is not
|
155
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
156
|
+
#
|
157
|
+
# CREDIT: David Heinemeier Hansson
|
158
|
+
#
|
159
|
+
# @uncommon
|
160
|
+
# require 'facets/module/mattr'
|
161
|
+
#
|
162
|
+
def mattr(*syms)
|
163
|
+
writers, readers = syms.flatten.partition{ |a| a.to_s =~ /=$/ }
|
164
|
+
writers = writers.collect{ |e| e.to_s.chomp('=').to_sym }
|
165
|
+
##readers.concat( writers ) # writers also get readers
|
166
|
+
|
167
|
+
mattr_writer( *writers )
|
168
|
+
mattr_reader( *readers )
|
169
|
+
|
170
|
+
return readers + writers
|
171
|
+
end
|
172
|
+
|
173
|
+
# Creates a class-variable attr_reader that can
|
174
|
+
# be accessed both on an instance and class level.
|
175
|
+
#
|
176
|
+
# c = Class.new do
|
177
|
+
# @@a = 10
|
178
|
+
# mattr_reader :a
|
179
|
+
# end
|
180
|
+
#
|
181
|
+
# c.a #=> 10
|
182
|
+
# c.new.a #=> 10
|
183
|
+
#
|
184
|
+
# NOTE: This methiod is not a common core extension and is not
|
185
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
186
|
+
#
|
187
|
+
# CREDIT: David Heinemeier Hansson
|
188
|
+
#
|
189
|
+
# @uncommon
|
190
|
+
# require 'facets/module/mattr'
|
191
|
+
#
|
192
|
+
def mattr_reader( *syms )
|
193
|
+
syms.flatten.each do |sym|
|
194
|
+
module_eval(<<-EOS, __FILE__, __LINE__)
|
195
|
+
unless defined? @@#{sym}
|
196
|
+
@@#{sym} = nil
|
197
|
+
end
|
198
|
+
|
199
|
+
def self.#{sym}
|
200
|
+
@@#{sym}
|
201
|
+
end
|
202
|
+
|
203
|
+
def #{sym}
|
204
|
+
@@#{sym}
|
205
|
+
end
|
206
|
+
EOS
|
207
|
+
end
|
208
|
+
return syms
|
209
|
+
end
|
210
|
+
|
211
|
+
# Creates a class-variable attr_writer that can
|
212
|
+
# be accessed both on an instance and class level.
|
213
|
+
#
|
214
|
+
# c = Class.new do
|
215
|
+
# mattr_writer :a
|
216
|
+
# def self.a
|
217
|
+
# @@a
|
218
|
+
# end
|
219
|
+
# end
|
220
|
+
#
|
221
|
+
# c.a = 10
|
222
|
+
# c.a #=> 10
|
223
|
+
#
|
224
|
+
# c.new.a = 29
|
225
|
+
# c.a #=> 29
|
226
|
+
#
|
227
|
+
# NOTE: This methiod is not a common core extension and is not
|
228
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
229
|
+
#
|
230
|
+
# CREDIT: David Heinemeier Hansson
|
231
|
+
#
|
232
|
+
# @uncommon
|
233
|
+
# require 'facets/module/mattr'
|
234
|
+
#
|
235
|
+
def mattr_writer(*syms)
|
236
|
+
syms.flatten.each do |sym|
|
237
|
+
module_eval(<<-EOS, __FILE__, __LINE__)
|
238
|
+
unless defined? @@#{sym}
|
239
|
+
@@#{sym} = nil
|
240
|
+
end
|
241
|
+
|
242
|
+
def self.#{sym}=(obj)
|
243
|
+
@@#{sym} = obj
|
244
|
+
end
|
245
|
+
|
246
|
+
def #{sym}=(obj)
|
247
|
+
@@#{sym}=(obj)
|
248
|
+
end
|
249
|
+
EOS
|
250
|
+
end
|
251
|
+
return syms
|
252
|
+
end
|
253
|
+
|
254
|
+
# Creates a class-variable attr_accessor that can
|
255
|
+
# be accessed both on an instance and class level.
|
256
|
+
#
|
257
|
+
# c = Class.new do
|
258
|
+
# mattr_accessor :a
|
259
|
+
# end
|
260
|
+
#
|
261
|
+
# c.a = 10
|
262
|
+
# c.a #=> 10
|
263
|
+
#
|
264
|
+
# x = c.new
|
265
|
+
# x.a #=> 10
|
266
|
+
#
|
267
|
+
# NOTE: This methiod is not a common core extension and is not
|
268
|
+
# loaded automatically when using <code>require 'facets'</code>.
|
269
|
+
#
|
270
|
+
# CREDIT: David Heinemeier Hansson
|
271
|
+
#
|
272
|
+
# @uncommon
|
273
|
+
# require 'facets/module/mattr'
|
274
|
+
#
|
275
|
+
def mattr_accessor(*syms)
|
276
|
+
mattr_reader(*syms) + mattr_writer(*syms)
|
277
|
+
end
|
278
|
+
|
279
|
+
end
|
280
|
+
|
281
|
+
# Consider the issue where a module's metaclass is not inherited.
|
282
|
+
#
|
283
|
+
# m = Module.new do
|
284
|
+
# @@a = 10
|
285
|
+
# mattr_reader :a
|
286
|
+
# end
|
287
|
+
#
|
288
|
+
# c = Class.new do
|
289
|
+
# include m
|
290
|
+
# end
|
291
|
+
#
|
292
|
+
# expect NoMethodError do
|
293
|
+
# c.a
|
294
|
+
# end
|
295
|
+
#
|
296
|
+
# Ideally this would work.
|
data/lib/defensor.rb
ADDED
@@ -0,0 +1,163 @@
|
|
1
|
+
|
2
|
+
#
|
3
|
+
# Defensio-Ruby
|
4
|
+
# Written by the Defensio team at Websense, Inc.
|
5
|
+
#
|
6
|
+
|
7
|
+
require 'rubygems'
|
8
|
+
require 'httparty'
|
9
|
+
require 'uri'
|
10
|
+
require 'json'
|
11
|
+
|
12
|
+
class Defensor
|
13
|
+
require 'defensor/cattr'
|
14
|
+
require "defensor/version"
|
15
|
+
include Version
|
16
|
+
|
17
|
+
class NoApiKeyException < Exception; end
|
18
|
+
class NoContentException < Exception; end
|
19
|
+
class NoSignatureException < Exception; end
|
20
|
+
|
21
|
+
cattr_accessor 'api_key'
|
22
|
+
ALLOWED_OPTIONS = [:type, :platform, "author-email", "author-ip", :content, :signature]
|
23
|
+
|
24
|
+
# You shouldn't modify these values unless you really know what you are doing. And then again...
|
25
|
+
API_VERSION = 2.0
|
26
|
+
API_HOST = "http://api.defensio.com"
|
27
|
+
|
28
|
+
# You should't modify anything below this line.
|
29
|
+
LIB_VERSION = Defensor::VERSION
|
30
|
+
ROOT_NODE = "defensio-result"
|
31
|
+
FORMAT = :json
|
32
|
+
USER_AGENT = "Defensor #{LIB_VERSION}"
|
33
|
+
CLIENT = "Defensor | #{LIB_VERSION} | Oleg Bovykin | oleg.bovykin@gmail.com"
|
34
|
+
KEEP_ALIVE = false
|
35
|
+
|
36
|
+
attr_accessor :options, :signature
|
37
|
+
|
38
|
+
include HTTParty
|
39
|
+
format FORMAT
|
40
|
+
base_uri API_HOST
|
41
|
+
|
42
|
+
def self.respond(response)
|
43
|
+
[response.code, response[ROOT_NODE]]
|
44
|
+
end
|
45
|
+
|
46
|
+
# Get information about the api key
|
47
|
+
def self.get_user
|
48
|
+
respond get(api_path)
|
49
|
+
end
|
50
|
+
|
51
|
+
def self.api_path(action = nil, id = nil)
|
52
|
+
path = "/#{API_VERSION}/users/#{@@api_key}"
|
53
|
+
path += "/#{action}" if action
|
54
|
+
path += "/#{id}" if id
|
55
|
+
path += ".#{FORMAT}"
|
56
|
+
end
|
57
|
+
|
58
|
+
# Get basic statistics for the current user
|
59
|
+
# @return [Array] An array containing 2 values: the HTTP status code & a Hash with the values returned by Defensio
|
60
|
+
def self.get_basic_stats
|
61
|
+
respond get(api_path("basic-stats"))
|
62
|
+
end
|
63
|
+
|
64
|
+
# Get more exhaustive statistics for the current user
|
65
|
+
# @param [Hash] data The parameters to be sent to Defensio. Keys can either be Strings or Symbols
|
66
|
+
# @return [Array] An array containing 2 values: the HTTP status code & a Hash with the values returned by Defensio
|
67
|
+
def self.get_extended_stats(data)
|
68
|
+
result = get(api_path("extended-stats"), :query => data)
|
69
|
+
code = result.code
|
70
|
+
result = result[ROOT_NODE]
|
71
|
+
|
72
|
+
0.upto(result["data"].size - 1) do |i|
|
73
|
+
result["data"][i]["date"] = Date.parse(result["data"][i]["date"])
|
74
|
+
end
|
75
|
+
|
76
|
+
[code, result]
|
77
|
+
end
|
78
|
+
|
79
|
+
# Filter a set of values based on a pre-defined dictionary
|
80
|
+
def self.post_profanity_filter(data)
|
81
|
+
respond post(api_path("profanity-filter"), :body => data)
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.parse_body(str)
|
85
|
+
if FORMAT == :json
|
86
|
+
return JSON.parse(str)[ROOT_NODE]
|
87
|
+
else
|
88
|
+
raise(NotImplementedError, "This library doesn't support this format: #{FORMAT}")
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Takes the request object (Rails, Sinatra, Merb) of an async request callback and returns a hash
|
93
|
+
# containing the status of the document being analyzed.
|
94
|
+
# @param [ActionController::Request, Sinatra::Request, String] request The request object created after Defensio POSTed to your site, or a string representation of the POST data.
|
95
|
+
# @return [Hash] Status of the document
|
96
|
+
def self.handle_post_document_async_callback(request)
|
97
|
+
if request.is_a?(String)
|
98
|
+
data = request
|
99
|
+
elsif request.respond_to?(:body) && request.body.respond_to?(:read)
|
100
|
+
data = request.body.read
|
101
|
+
else
|
102
|
+
raise ArgumentError, "Unknown request type: #{request.class}"
|
103
|
+
end
|
104
|
+
|
105
|
+
Defensor.parse_body(data)
|
106
|
+
end
|
107
|
+
|
108
|
+
def initialize(*options)
|
109
|
+
check_key
|
110
|
+
@options = options[0].reject {|k, v| !(ALLOWED_OPTIONS.include? k)}
|
111
|
+
@signature = @options['signature'] if @options['signature']
|
112
|
+
end
|
113
|
+
|
114
|
+
def check_key
|
115
|
+
raise NoApiKeyException if @@api_key.nil? || @@api_key.empty?
|
116
|
+
end
|
117
|
+
|
118
|
+
def check_document
|
119
|
+
raise NoContentException if content.nil? || content.empty?
|
120
|
+
end
|
121
|
+
|
122
|
+
def content
|
123
|
+
@options[:content]
|
124
|
+
end
|
125
|
+
|
126
|
+
# Create and analyze a new document
|
127
|
+
# @param [Hash] data The parameters to be sent to Defensio. Keys can either be Strings or Symbols
|
128
|
+
# @return [Array] An array containing 2 values: the HTTP status code & a Hash with the values returned by Defensio
|
129
|
+
def post_document
|
130
|
+
check_document
|
131
|
+
response = Defensor.post(Defensor.api_path("documents"), :body => { :client => CLIENT }.merge(@options))
|
132
|
+
status = response[ROOT_NODE]["status"]
|
133
|
+
@signature = response[ROOT_NODE]["signature"] if status == "success"
|
134
|
+
Defensor.respond response
|
135
|
+
end
|
136
|
+
|
137
|
+
# Get the status of an existing document
|
138
|
+
# @param [String] signature The signature of the document to retrieve
|
139
|
+
# @return [Array] An array containing 2 values: the HTTP status code & a Hash with the values returned by Defensio
|
140
|
+
def get_document(signature=nil)
|
141
|
+
@signature ||= signature
|
142
|
+
if @signature
|
143
|
+
Defensor.respond Defensor.get(Defensor.api_path("documents", @signature))
|
144
|
+
else
|
145
|
+
raise NoSignatureException
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
# Modify the properties of an existing document
|
150
|
+
# @param [String] signature The signature of the document to modify
|
151
|
+
# @param [Hash] data The parameters to be sent to Defensio. Keys can either be Strings or Symbols
|
152
|
+
# @return [Array] An array containing 2 values: the HTTP status code & a Hash with the values returned by Defensio
|
153
|
+
def put_document(signature=nil, data)
|
154
|
+
@signature ||= signature
|
155
|
+
if @signature
|
156
|
+
Defensor.respond Defensor.put(Defensor.api_path("documents", @signature), :body => data)
|
157
|
+
else
|
158
|
+
raise NoSignatureException
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
end
|
163
|
+
|
@@ -0,0 +1,178 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Defensor do
|
6
|
+
before { Defensor.api_key = ENV['DEFENSIO_KEY'] }
|
7
|
+
|
8
|
+
context "initialize" do
|
9
|
+
let(:defensor_no_key) { Defensor.api_key = nil; Defensor.new({}) }
|
10
|
+
let(:defensor) { Defensor.api_key = "ABC"; Defensor.new({type: :forum, platform: :movable_type, nonrelevant: :abc}) }
|
11
|
+
|
12
|
+
it "Accepts input params with key" do
|
13
|
+
expect(defensor.options.keys).not_to include "nonrelevant"
|
14
|
+
end
|
15
|
+
|
16
|
+
it "Throws Exception if api_key not set" do
|
17
|
+
lambda { defensor_no_key }.should raise_exception(Defensor::NoApiKeyException)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
context ".api_path" do
|
22
|
+
|
23
|
+
it "returns valid url with no options" do
|
24
|
+
expect(Defensor.api_path).to eq "/#{Defensor::API_VERSION}/users/#{Defensor.api_key}.json"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns valid url with action" do
|
28
|
+
expect(Defensor.api_path("action")).to eq "/#{Defensor::API_VERSION}/users/#{Defensor.api_key}/action.json"
|
29
|
+
end
|
30
|
+
|
31
|
+
it "returns valid url with action and id" do
|
32
|
+
expect(Defensor.api_path("action", "id")).to eq "/#{Defensor::API_VERSION}/users/#{Defensor.api_key}/action/id.json"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
if ENV['DEFENSIO_KEY']
|
37
|
+
|
38
|
+
context "real_requests" do
|
39
|
+
before { Defensor.api_key = ENV['DEFENSIO_KEY'] }
|
40
|
+
|
41
|
+
context ".get_user" do
|
42
|
+
let(:get_user) { Defensor.get_user }
|
43
|
+
|
44
|
+
it "returns valid response" do
|
45
|
+
status, body = get_user
|
46
|
+
expect(body.is_a? Hash).to eq true
|
47
|
+
expect(status).to eq 200
|
48
|
+
expect(body['status']).to eq "success"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context ".get_basic_stats" do
|
53
|
+
let(:stats) { Defensor.get_basic_stats }
|
54
|
+
|
55
|
+
it "returns valid result" do
|
56
|
+
status, body = stats
|
57
|
+
expect(body.is_a? Hash).to eq true
|
58
|
+
expect(status).to eq 200
|
59
|
+
expect(body['status']).to eq "success"
|
60
|
+
expect(body["unwanted"]["total"].is_a? Integer).to eq true
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context ".get_extended_stats" do
|
65
|
+
let(:stats) { Defensor.get_extended_stats(:from => Date.new(2009, 9, 1), :to => Date.new(2009, 9, 3)) }
|
66
|
+
|
67
|
+
it "returns valid result" do
|
68
|
+
status, body = stats
|
69
|
+
expect(body.is_a? Hash).to eq true
|
70
|
+
expect(status).to eq 200
|
71
|
+
expect(body['data'].is_a? Array).to eq true
|
72
|
+
expect(body["data"][0]["date"].is_a? Date ).to eq true if body["data"].size > 0
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context ".post_profanity_filter" do
|
77
|
+
let(:stats) { Defensor.post_profanity_filter("field1"=>"hello world", "other_field"=>"hello again") }
|
78
|
+
|
79
|
+
it "returns valid result" do
|
80
|
+
status, body = stats
|
81
|
+
expect(body.is_a? Hash).to eq true
|
82
|
+
expect(status).to eq 200
|
83
|
+
expect(body["filtered"].is_a? Hash).to eq true
|
84
|
+
expect(body["filtered"].keys).to include "field1"
|
85
|
+
expect(body["filtered"].keys).to include "other_field"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
context ".parse_body" do
|
90
|
+
let(:body) { Defensor.parse_body '{"defensio-result":{"hello":"world"}}'}
|
91
|
+
let(:result) { {"hello" => "world"} }
|
92
|
+
|
93
|
+
it "returns valid result" do
|
94
|
+
expect(body).to eq result
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context "#post_document" do
|
99
|
+
let(:defensor) { Defensor.new({type: :forum, platform: :movable_type, nonrelevant: :abc, content: "My test content for defensor"}) }
|
100
|
+
let(:response) { defensor.post_document }
|
101
|
+
|
102
|
+
it "puts document to defensio and sets signature" do
|
103
|
+
status, body = response
|
104
|
+
expect(defensor.signature).to eq body["signature"]
|
105
|
+
expect(status).to eq 200
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context ".handle_post_document_async_callback__string" do
|
110
|
+
require 'ostruct'
|
111
|
+
let(:result) { { "defensio-result" =>
|
112
|
+
{ "api-version" => Defensor::API_VERSION,
|
113
|
+
"status" => "success",
|
114
|
+
"message" => nil,
|
115
|
+
"signature" => "123456",
|
116
|
+
"allow" => false,
|
117
|
+
"classification" => "malicious",
|
118
|
+
"spaminess" => 0.95,
|
119
|
+
"profanity-match" => true }
|
120
|
+
} }
|
121
|
+
let(:request) { OpenStruct.new(:body => StringIO.new(result.to_json)) }
|
122
|
+
|
123
|
+
it "checks string" do
|
124
|
+
expect(Defensor.handle_post_document_async_callback(result.to_json).class).to eq Hash
|
125
|
+
end
|
126
|
+
|
127
|
+
it "checks object" do
|
128
|
+
result = Defensor.handle_post_document_async_callback(request)
|
129
|
+
expect(result.class).to eq Hash
|
130
|
+
expect(result["status"]).to eq "success"
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "integration" do
|
135
|
+
let(:defensor) { Defensor.api_key = ENV['DEFENSIO_KEY']; Defensor.new({:content => "This is a simple test", :platform => "
|
136
|
+
my_test_platform", :type => "comment"}) }
|
137
|
+
let(:post) { defensor.post_document }
|
138
|
+
|
139
|
+
it "test post get put" do
|
140
|
+
status, body = post
|
141
|
+
expect(body.is_a? Hash).to eq true
|
142
|
+
expect(status).to eq 200
|
143
|
+
expect(body["status"]).to eq "success"
|
144
|
+
expect(defensor.signature).to eq body["signature"]
|
145
|
+
|
146
|
+
# Keep some variables around
|
147
|
+
original_allow_result = body["allow"]
|
148
|
+
|
149
|
+
sleep 0.5
|
150
|
+
|
151
|
+
# GET
|
152
|
+
status, body = defensor.get_document
|
153
|
+
expect(body.is_a? Hash).to eq true
|
154
|
+
expect(status).to eq 200
|
155
|
+
expect(body["status"]).to eq "success"
|
156
|
+
expect(defensor.signature).to eq body["signature"]
|
157
|
+
|
158
|
+
# PUT
|
159
|
+
status, body = defensor.put_document(:allow => !original_allow_result)
|
160
|
+
expect(body.is_a? Hash).to eq true
|
161
|
+
expect(status).to eq 200
|
162
|
+
expect(body["status"]).to eq "success"
|
163
|
+
expect(defensor.signature).to eq body["signature"]
|
164
|
+
expect(body["allow"]).to eq !original_allow_result
|
165
|
+
|
166
|
+
status, body = defensor.put_document(:allow => original_allow_result)
|
167
|
+
expect(body.is_a? Hash).to eq true
|
168
|
+
expect(status).to eq 200
|
169
|
+
expect(body["status"]).to eq "success"
|
170
|
+
expect(defensor.signature).to eq body["signature"]
|
171
|
+
expect(body["allow"]).to eq original_allow_result
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
end # context "real_requests"
|
176
|
+
end # if ENV['DEFENSIO_KEY']
|
177
|
+
|
178
|
+
end
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,189 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: defensor
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.11.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Oleg Bovykin
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-16 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: bundler
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '1.3'
|
30
|
+
- !ruby/object:Gem::Dependency
|
31
|
+
name: rake
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
33
|
+
none: false
|
34
|
+
requirements:
|
35
|
+
- - ! '>='
|
36
|
+
- !ruby/object:Gem::Version
|
37
|
+
version: '0'
|
38
|
+
type: :development
|
39
|
+
prerelease: false
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
41
|
+
none: false
|
42
|
+
requirements:
|
43
|
+
- - ! '>='
|
44
|
+
- !ruby/object:Gem::Version
|
45
|
+
version: '0'
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: mocha
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :development
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: rspec
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
none: false
|
66
|
+
requirements:
|
67
|
+
- - ! '>='
|
68
|
+
- !ruby/object:Gem::Version
|
69
|
+
version: '0'
|
70
|
+
type: :development
|
71
|
+
prerelease: false
|
72
|
+
version_requirements: !ruby/object:Gem::Requirement
|
73
|
+
none: false
|
74
|
+
requirements:
|
75
|
+
- - ! '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
- !ruby/object:Gem::Dependency
|
79
|
+
name: guard-rspec
|
80
|
+
requirement: !ruby/object:Gem::Requirement
|
81
|
+
none: false
|
82
|
+
requirements:
|
83
|
+
- - ! '>='
|
84
|
+
- !ruby/object:Gem::Version
|
85
|
+
version: '0'
|
86
|
+
type: :development
|
87
|
+
prerelease: false
|
88
|
+
version_requirements: !ruby/object:Gem::Requirement
|
89
|
+
none: false
|
90
|
+
requirements:
|
91
|
+
- - ! '>='
|
92
|
+
- !ruby/object:Gem::Version
|
93
|
+
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: json
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :runtime
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
- !ruby/object:Gem::Dependency
|
111
|
+
name: facets
|
112
|
+
requirement: !ruby/object:Gem::Requirement
|
113
|
+
none: false
|
114
|
+
requirements:
|
115
|
+
- - ! '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
type: :runtime
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
none: false
|
122
|
+
requirements:
|
123
|
+
- - ! '>='
|
124
|
+
- !ruby/object:Gem::Version
|
125
|
+
version: '0'
|
126
|
+
- !ruby/object:Gem::Dependency
|
127
|
+
name: httparty
|
128
|
+
requirement: !ruby/object:Gem::Requirement
|
129
|
+
none: false
|
130
|
+
requirements:
|
131
|
+
- - ! '>='
|
132
|
+
- !ruby/object:Gem::Version
|
133
|
+
version: '0'
|
134
|
+
type: :runtime
|
135
|
+
prerelease: false
|
136
|
+
version_requirements: !ruby/object:Gem::Requirement
|
137
|
+
none: false
|
138
|
+
requirements:
|
139
|
+
- - ! '>='
|
140
|
+
- !ruby/object:Gem::Version
|
141
|
+
version: '0'
|
142
|
+
description: Unofficial Ruby library for Defensio 2.0
|
143
|
+
email:
|
144
|
+
- oleg.bovykin@gmail.com
|
145
|
+
executables: []
|
146
|
+
extensions: []
|
147
|
+
extra_rdoc_files: []
|
148
|
+
files:
|
149
|
+
- .gitignore
|
150
|
+
- .rspec
|
151
|
+
- Gemfile
|
152
|
+
- Guardfile
|
153
|
+
- LICENSE.txt
|
154
|
+
- README.md
|
155
|
+
- Rakefile
|
156
|
+
- defensor.gemspec
|
157
|
+
- lib/defensor.rb
|
158
|
+
- lib/defensor/cattr.rb
|
159
|
+
- lib/defensor/version.rb
|
160
|
+
- spec/models/defensor_spec.rb
|
161
|
+
- spec/spec_helper.rb
|
162
|
+
homepage: ''
|
163
|
+
licenses:
|
164
|
+
- MIT
|
165
|
+
post_install_message:
|
166
|
+
rdoc_options: []
|
167
|
+
require_paths:
|
168
|
+
- lib
|
169
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
170
|
+
none: false
|
171
|
+
requirements:
|
172
|
+
- - ! '>='
|
173
|
+
- !ruby/object:Gem::Version
|
174
|
+
version: '0'
|
175
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
176
|
+
none: false
|
177
|
+
requirements:
|
178
|
+
- - ! '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
requirements: []
|
182
|
+
rubyforge_project:
|
183
|
+
rubygems_version: 1.8.25
|
184
|
+
signing_key:
|
185
|
+
specification_version: 3
|
186
|
+
summary: Unofficial Ruby library for Defensio 2.0
|
187
|
+
test_files:
|
188
|
+
- spec/models/defensor_spec.rb
|
189
|
+
- spec/spec_helper.rb
|