defender 0.1.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/.document +5 -0
- data/.gitignore +23 -0
- data/LICENSE +20 -0
- data/README.rdoc +24 -0
- data/Rakefile +69 -0
- data/VERSION +1 -0
- data/lib/defender.rb +328 -0
- data/spec/defender_spec.rb +79 -0
- data/spec/spec.opts +1 -0
- data/spec/spec_helper.rb +9 -0
- metadata +85 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 Henrik Hodne
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
= defender
|
2
|
+
|
3
|
+
This is a Ruby wrapper of the Defensio[http://defensio.com] spam filtering API. To use this library, you need an API key from Defensio. Go ahead and {get one}[http://defensio.com/signup/].
|
4
|
+
|
5
|
+
== Testing
|
6
|
+
|
7
|
+
To run the tests, you need to pass the api key and owner url with the environment keys API_KEY and API_OWNER_URL, like this:
|
8
|
+
rake spec API_KEY="key1234" API_OWNER_URL="http://myblog.com"
|
9
|
+
The tests will fail with invalid keys and urls.
|
10
|
+
|
11
|
+
== Note on Patches/Pull Requests
|
12
|
+
|
13
|
+
* Fork the project.
|
14
|
+
* Make your feature addition or bug fix.
|
15
|
+
* Add tests for it. This is important so I don't break it in a
|
16
|
+
future version unintentionally.
|
17
|
+
* Commit, do not mess with rakefile, version, or history.
|
18
|
+
(if you want to have your own version, that is fine but
|
19
|
+
bump version in a commit by itself I can ignore when I pull)
|
20
|
+
* Send me a pull request. Bonus points for topic branches.
|
21
|
+
|
22
|
+
== Copyright
|
23
|
+
|
24
|
+
Copyright (c) 2009 Henrik Hodne. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = "defender"
|
8
|
+
gem.summary = %Q{Ruby API wrapper for Defensio}
|
9
|
+
gem.description = %Q{A wrapper of the Defensio spam filtering service.}
|
10
|
+
gem.email = "henrik.hodne@binaryhex.com"
|
11
|
+
gem.homepage = "http://github.com/dvyjones/defender"
|
12
|
+
gem.authors = ["Henrik Hodne"]
|
13
|
+
gem.add_development_dependency "rspec", ">= 1.2.9"
|
14
|
+
gem.add_development_dependency "yard", ">= 0"
|
15
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
16
|
+
end
|
17
|
+
Jeweler::GemcutterTasks.new
|
18
|
+
rescue LoadError
|
19
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
20
|
+
end
|
21
|
+
|
22
|
+
require 'spec/rake/spectask'
|
23
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
24
|
+
spec.libs << 'lib' << 'spec'
|
25
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
26
|
+
end
|
27
|
+
|
28
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
29
|
+
spec.libs << 'lib' << 'spec'
|
30
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
31
|
+
spec.rcov = true
|
32
|
+
end
|
33
|
+
|
34
|
+
task :spec => :check_dependencies
|
35
|
+
|
36
|
+
begin
|
37
|
+
require 'reek/adapters/rake_task'
|
38
|
+
Reek::RakeTask.new do |t|
|
39
|
+
t.verbose = false
|
40
|
+
t.source_files = 'lib/**/*.rb'
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
task :reek do
|
44
|
+
abort "Reek is not available. In order to run reek, you must: sudo gem install reek"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
begin
|
49
|
+
require 'roodi'
|
50
|
+
require 'roodi_task'
|
51
|
+
RoodiTask.new do |t|
|
52
|
+
t.verbose = false
|
53
|
+
end
|
54
|
+
rescue LoadError
|
55
|
+
task :roodi do
|
56
|
+
abort "Roodi is not available. In order to run roodi, you must: sudo gem install roodi"
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
task :default => :spec
|
61
|
+
|
62
|
+
begin
|
63
|
+
require 'yard'
|
64
|
+
YARD::Rake::YardocTask.new
|
65
|
+
rescue LoadError
|
66
|
+
task :yardoc do
|
67
|
+
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
68
|
+
end
|
69
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.0
|
data/lib/defender.rb
ADDED
@@ -0,0 +1,328 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'net/http'
|
3
|
+
|
4
|
+
class Defender
|
5
|
+
# The Defensio API version currently supported by Defender
|
6
|
+
API_VERSION = "1.2"
|
7
|
+
|
8
|
+
DEFAULT_OPTIONS = {
|
9
|
+
service_type: "blog",
|
10
|
+
api_key: "",
|
11
|
+
owner_url: ""
|
12
|
+
}
|
13
|
+
|
14
|
+
##
|
15
|
+
# Raised if an invalid or no API key is given
|
16
|
+
class APIKeyError < StandardError; end
|
17
|
+
|
18
|
+
##
|
19
|
+
# The response from the {Defender#audit_comment} method. Should only be
|
20
|
+
# initialized by the library.
|
21
|
+
class CommentResponse
|
22
|
+
##
|
23
|
+
# A message signature that uniquely identifies the comment in the Defensio
|
24
|
+
# system. This signature should be stored by the client for retraining
|
25
|
+
# purposes.
|
26
|
+
attr_reader :signature
|
27
|
+
|
28
|
+
##
|
29
|
+
# A value indicating the relative likelihood of the comment being spam.
|
30
|
+
# This value should be stored by the client for use in building convenient
|
31
|
+
# spam sorting user-interfaces.
|
32
|
+
#
|
33
|
+
# @return [Float] A value between 0 and 1.
|
34
|
+
attr_reader :spaminess
|
35
|
+
|
36
|
+
##
|
37
|
+
# Initialize a CommentResponse. Should only be called by the library.
|
38
|
+
#
|
39
|
+
# @param [Hash] response The response from the audit-comment call.
|
40
|
+
def initialize(response)
|
41
|
+
@signature = response["signature"]
|
42
|
+
@spam = response["spam"]
|
43
|
+
@spaminess = response["spaminess"].to_f
|
44
|
+
end
|
45
|
+
|
46
|
+
##
|
47
|
+
# Returns true if Defensio marked the comment as spam, returns false
|
48
|
+
# otherwise.
|
49
|
+
#
|
50
|
+
# @return [Boolean]
|
51
|
+
def spam?
|
52
|
+
@spam
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
@signature
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
##
|
61
|
+
# The response from the {Defender#statistics} method. Should only be
|
62
|
+
# initialized by the library.
|
63
|
+
class Statistics
|
64
|
+
##
|
65
|
+
# Describes the percentage of comments correctly identified as spam/ham by
|
66
|
+
# Defensio on this blog.
|
67
|
+
#
|
68
|
+
# @return [Float<0..1>]
|
69
|
+
attr_reader :accuracy
|
70
|
+
|
71
|
+
##
|
72
|
+
# The number of spam comments caught by the filter.
|
73
|
+
attr_reader :spam
|
74
|
+
|
75
|
+
##
|
76
|
+
# The number of ham (legitimate) comments accepted by the filter.
|
77
|
+
attr_reader :ham
|
78
|
+
|
79
|
+
##
|
80
|
+
# The number of times a legitimate message was retrained from the spambox
|
81
|
+
# (i.e. "de-spammed" by the user)
|
82
|
+
attr_reader :false_positives
|
83
|
+
|
84
|
+
##
|
85
|
+
# The number of times a spam message was retrained from comments box (i.e.
|
86
|
+
# "de-legitimized" by the user)
|
87
|
+
attr_reader :false_negatives
|
88
|
+
|
89
|
+
##
|
90
|
+
# A boolean value indicating whether Defensio is still in its initial
|
91
|
+
# learning phase.
|
92
|
+
#
|
93
|
+
# @return [Boolean]
|
94
|
+
attr_reader :learning
|
95
|
+
|
96
|
+
##
|
97
|
+
# More details on the reason(s) why Defensio is still in its initial
|
98
|
+
# learning phase.
|
99
|
+
attr_reader :learning_status
|
100
|
+
|
101
|
+
def initialize(response)
|
102
|
+
@accuracy = response["accuracy"]
|
103
|
+
@spam = response["spam"]
|
104
|
+
@ham = response["ham"]
|
105
|
+
@false_positives = response["false-positives"]
|
106
|
+
@false_negatives = response["false-negatives"]
|
107
|
+
@learning = response["learning"]
|
108
|
+
@learning_status = response["learning-status"]
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
attr_accessor :service_type, :api_key, :owner_url
|
113
|
+
|
114
|
+
##
|
115
|
+
# Raises a StandardError with the error message from Defensio if the
|
116
|
+
# response is a "failed" one.
|
117
|
+
#
|
118
|
+
# @param [Hash] response The return value from {#call_action}.
|
119
|
+
def self.raise_if_error(response)
|
120
|
+
if response["status"] == "fail"
|
121
|
+
raise StandardError, response["message"]
|
122
|
+
end
|
123
|
+
response
|
124
|
+
end
|
125
|
+
|
126
|
+
##
|
127
|
+
# Converts a hash with symbol keys and underscores to a hash with string
|
128
|
+
# keys and hyphens. Calls #strftime or #to_s on the values.
|
129
|
+
#
|
130
|
+
# @param [Hash] options Input options.
|
131
|
+
# @return [Hash]
|
132
|
+
def self.options_to_parameters(options)
|
133
|
+
opts = {}
|
134
|
+
options.each do |key, value|
|
135
|
+
if value.respond_to?(:strftime)
|
136
|
+
value = value.strftime("%Y/%m/%d")
|
137
|
+
end
|
138
|
+
opts[key.to_s.gsub("_", "-").downcase] = value.to_s
|
139
|
+
end
|
140
|
+
opts
|
141
|
+
end
|
142
|
+
|
143
|
+
##
|
144
|
+
# Initialize Defender
|
145
|
+
#
|
146
|
+
# @param [Hash] opts The options hash.
|
147
|
+
# @option opts ["blog","app"] :service_type ("blog") The service type. May be
|
148
|
+
# "app" (use of Defender within an application) or "blog" (use of Defender
|
149
|
+
# to support a blogging platform).
|
150
|
+
# @option opts [String] :api_key Your API key. This option is required, the
|
151
|
+
# method calls will fail without it.
|
152
|
+
def initialize(opts={})
|
153
|
+
opts = DEFAULT_OPTIONS.merge(opts)
|
154
|
+
@service_type = opts[:service_type]
|
155
|
+
@api_key = opts[:api_key]
|
156
|
+
@owner_url = opts[:owner_url]
|
157
|
+
end
|
158
|
+
|
159
|
+
##
|
160
|
+
# Checks if the given key is valid.
|
161
|
+
#
|
162
|
+
# @return [Boolean]
|
163
|
+
# @see http://defensio.com/api/#validate-key
|
164
|
+
def valid_key?
|
165
|
+
begin
|
166
|
+
response = call_action("validate-key")
|
167
|
+
if response["status"] == "success"
|
168
|
+
return true
|
169
|
+
else
|
170
|
+
return false
|
171
|
+
end
|
172
|
+
rescue StandardError
|
173
|
+
return false
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
##
|
178
|
+
# Announce an article existence. This should (if feasible) be called when an
|
179
|
+
# article or blogpost is created so Defensio can analyse it.
|
180
|
+
#
|
181
|
+
# @param [#to_s] title The title of the article
|
182
|
+
# @param [#to_s] author The name of the author of the article
|
183
|
+
# @param [#to_s] author_email The email address of the person posting the
|
184
|
+
# article.
|
185
|
+
# @param [#to_s] content The content of the article itself.
|
186
|
+
# @param [#to_s] permalink The permalink of the article just posted.
|
187
|
+
# @raise [StandardError] If the call fails, a StandardError is raised with
|
188
|
+
# the error message given from Defensio.
|
189
|
+
# @return [Boolean] Returns true if the article was successfully announced,
|
190
|
+
# raises StandardError otherwise.
|
191
|
+
# @see http://defensio.com/api/#announce-article
|
192
|
+
def announce_article(title, author, author_email, content, permalink)
|
193
|
+
response = call_action("announce-article",
|
194
|
+
"article-title" => title.to_s,
|
195
|
+
"article-author" => author.to_s,
|
196
|
+
"article-author-email" => author_email.to_s,
|
197
|
+
"article-content" => content,
|
198
|
+
"permalink" => permalink)
|
199
|
+
true
|
200
|
+
end
|
201
|
+
|
202
|
+
##
|
203
|
+
# Check if a comment is spam. This is the central action of Defensio.
|
204
|
+
#
|
205
|
+
# @param [Hash] opts All options are recommended, but only required if noted.
|
206
|
+
# @option opts [#to_s] :user_ip The IP address of whomever is posting the
|
207
|
+
# comment. This option is required.
|
208
|
+
# @option opts [#to_s, #strftime] :article_date The date the original blog
|
209
|
+
# article was posted. If a string is given, it must be in the format
|
210
|
+
# "yyyy/mm/dd". This option is required.
|
211
|
+
# @option opts [#to_s] :comment_author The name of the author of the comment.
|
212
|
+
# This option is required.
|
213
|
+
# @option opts ["comment", "trackback", "pingback", "other"] :comment_type
|
214
|
+
# The type of the comment being posted to the blog. This option is required
|
215
|
+
# @option opts [#to_s] :comment_content The actual content of the comment
|
216
|
+
# (strongly recommended to be included where ever possible).
|
217
|
+
# @option opts [#to_s] :comment_author_email The email address of the person
|
218
|
+
# posting the comment.
|
219
|
+
# @option opts [#to_s] :comment_author_url The URL of the person posting the
|
220
|
+
# comment.
|
221
|
+
# @option opts [#to_s] :permalink The permalink of the blog post to which
|
222
|
+
# the comment is being posted.
|
223
|
+
# @option opts [#to_s] :referrer The URL of the site that brought commenter
|
224
|
+
# to this page.
|
225
|
+
# @option opts [Boolean] :user_logged_in Whether or not the user posting
|
226
|
+
# the comment is logged-into the blogging platform
|
227
|
+
# @option opts [Boolean] :trusted_user Whether or not the user is an
|
228
|
+
# administrator, moderator or editor of this blog; the client should pass
|
229
|
+
# true only if blogging platform can guarantee that the user has been
|
230
|
+
# authenticated and has a role of responsibility on this blog.
|
231
|
+
# @option opts [#to_s] :openid The OpenID URL of the currently logged in
|
232
|
+
# user. Must be used in conjunction with :user_logged_in => true. OpenID
|
233
|
+
# authentication must be taken care of by your application.
|
234
|
+
# @option opts [#to_s] :test_force For testing purposes only: Use this
|
235
|
+
# parameter to force the outcome of audit_comment. Optionally affix (with
|
236
|
+
# a comma) a desired spaminess return value (in the range 0 to 1).
|
237
|
+
# Example: "spam,0.5000" or "ham,0.0010".
|
238
|
+
# @raise [StandardError] If the call fails, a StandardError is raised with
|
239
|
+
# the error message given from Defensio.
|
240
|
+
# @return [Defender::CommentResponse]
|
241
|
+
# @see http://defensio.com/api/#audit-comment
|
242
|
+
def audit_comment(opts={})
|
243
|
+
response = call_action("audit-comment", Defender.options_to_parameters(opts))
|
244
|
+
return CommentResponse.new(response)
|
245
|
+
end
|
246
|
+
|
247
|
+
##
|
248
|
+
# This action is used to retrain false negatives. False negatives are
|
249
|
+
# comments that were originally tagged as "ham" (i.e. legitimate) but were
|
250
|
+
# in fact spam.
|
251
|
+
#
|
252
|
+
# @param [Array<#to_s, CommentResponse>] signatures List of signatures (may
|
253
|
+
# contain a single entry) of the comments to be submitted for retraining.
|
254
|
+
# Note that a signature for each comment was originally provided by the
|
255
|
+
# {#audit_comment} method.
|
256
|
+
# @raise [StandardError] If the call fails, a StandardError is raised with
|
257
|
+
# the error message given from Defensio.
|
258
|
+
# @return [Boolean] Returns true if the comments were successfully marked,
|
259
|
+
# raises StandardError otherwise.
|
260
|
+
def report_false_negatives(signatures)
|
261
|
+
response = call_action("report-false-negatives",
|
262
|
+
"signatures" => signatures.map(&:to_s).join(","))
|
263
|
+
true
|
264
|
+
end
|
265
|
+
|
266
|
+
##
|
267
|
+
# This action is used to retrain false negatives. False negatives are
|
268
|
+
# comments that were originally tagged as spam but were in fact "ham" (i.e.
|
269
|
+
# legitimate).
|
270
|
+
#
|
271
|
+
# @param [Array<#to_s, CommentResponse>] signatures List of signatures (may
|
272
|
+
# contain a single entry) of the comments to be submitted for retraining.
|
273
|
+
# Note that a signature for each comment was originally provided by the
|
274
|
+
# {#audit_comment} method.
|
275
|
+
# @raise [StandardError] If the call fails, a StandardError is raised with
|
276
|
+
# the error message given from Defensio.
|
277
|
+
# @return [Boolean] Returns true if the comments were successfully marked,
|
278
|
+
# raises StandardError otherwise.
|
279
|
+
def report_false_positives(signatures)
|
280
|
+
response = call_action("report-false-positives",
|
281
|
+
"signatures" => signatures.map(&:to_s).join(","))
|
282
|
+
true
|
283
|
+
end
|
284
|
+
|
285
|
+
##
|
286
|
+
# This action returns basic statistics regarding the performance of Defensio
|
287
|
+
# since activation.
|
288
|
+
#
|
289
|
+
# @return [Defender::Statistics]
|
290
|
+
def statistics
|
291
|
+
response = call_action("get-stats")
|
292
|
+
return Statistics.new(response)
|
293
|
+
end
|
294
|
+
|
295
|
+
private
|
296
|
+
##
|
297
|
+
# Returns the url for the given action.
|
298
|
+
#
|
299
|
+
# @param [#to_s] action The action to generate the URL for.
|
300
|
+
# @return [String] The URL for the action.
|
301
|
+
# @raise [APIKeyError] Raises this if no API key is given.
|
302
|
+
def url(action)
|
303
|
+
raise APIKeyError unless @api_key.length > 0
|
304
|
+
"http://api.defensio.com/" \
|
305
|
+
"#{@service_type}/" \
|
306
|
+
"#{Defender::API_VERSION}/" \
|
307
|
+
"#{action}/" \
|
308
|
+
"#{@api_key}.yaml"
|
309
|
+
end
|
310
|
+
|
311
|
+
##
|
312
|
+
# Backend function for calling an action.
|
313
|
+
#
|
314
|
+
# @param [#to_s] action The action to call.
|
315
|
+
# @param [Hash] params The parameters for the action.
|
316
|
+
# @return [Hash] The raw response, only parsed from YAML.
|
317
|
+
# @raise [APIKeyError] If an invalid (or no) API key is given, this is
|
318
|
+
# raised
|
319
|
+
def call_action(action, params={})
|
320
|
+
params = {"owner-url" => @owner_url}.merge(params)
|
321
|
+
response = Net::HTTP.post_form(URI.parse(url(action)), params)
|
322
|
+
if response.code == 400
|
323
|
+
raise APIKeyError
|
324
|
+
else
|
325
|
+
Defender.raise_if_error(YAML.load(response.body)["defensio-result"])
|
326
|
+
end
|
327
|
+
end
|
328
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
|
2
|
+
|
3
|
+
describe "Defender" do
|
4
|
+
it "should raise a StandardError if a method fails" do
|
5
|
+
lambda do
|
6
|
+
Defender.raise_if_error({
|
7
|
+
"status" => "fail",
|
8
|
+
"message" => "Failed!"
|
9
|
+
})
|
10
|
+
end.should raise_error(StandardError, "Failed!")
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should return the correct URL for any given action" do
|
14
|
+
d = Defender.new(:api_key => "key1234")
|
15
|
+
d.instance_eval do
|
16
|
+
url("foobar")
|
17
|
+
end.should == "http://api.defensio.com/blog/#{Defender::API_VERSION}/foobar/key1234.yaml"
|
18
|
+
end
|
19
|
+
|
20
|
+
it "should correctly identify a valid API key" do
|
21
|
+
d = Defender.new(:api_key => ENV["API_KEY"], :owner_url => ENV["API_OWNER_URL"])
|
22
|
+
d.valid_key?.should be_true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should correctly identify an invalid API key" do
|
26
|
+
d = Defender.new(:api_key => "key1234", :owner_url => ENV["API_OWNER_URL"])
|
27
|
+
d.valid_key?.should be_false
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should correctly identify a spammy comment" do
|
31
|
+
d = Defender.new(:api_key => ENV["API_KEY"], :owner_url => ENV["API_OWNER_URL"])
|
32
|
+
|
33
|
+
d.audit_comment(
|
34
|
+
:user_ip => "127.0.0.1",
|
35
|
+
:article_date => Time.now,
|
36
|
+
:comment_author => "Henrik Hodne",
|
37
|
+
:comment_type => "comment",
|
38
|
+
:test_force => "spam,0.5000"
|
39
|
+
).spam?.should be_true
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should correctly identify a meaty comment" do
|
43
|
+
d = Defender.new(:api_key => ENV["API_KEY"], :owner_url => ENV["API_OWNER_URL"])
|
44
|
+
|
45
|
+
d.audit_comment(
|
46
|
+
:user_ip => "127.0.0.1",
|
47
|
+
:article_date => Time.now,
|
48
|
+
:comment_author => "Henrik Hodne",
|
49
|
+
:comment_type => "comment",
|
50
|
+
:test_force => "ham,0.1000"
|
51
|
+
).spam?.should be_false
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should correctly set the spaminess" do
|
55
|
+
d = Defender.new(:api_key => ENV["API_KEY"], :owner_url => ENV["API_OWNER_URL"])
|
56
|
+
|
57
|
+
d.audit_comment(
|
58
|
+
:user_ip => "127.0.0.1",
|
59
|
+
:article_date => Time.now,
|
60
|
+
:comment_author => "Henrik Hodne",
|
61
|
+
:comment_type => "comment",
|
62
|
+
:test_force => "spam,0.5000"
|
63
|
+
).spaminess.should == 0.5
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should fail without valid API credentials" do
|
67
|
+
d = Defender.new(:api_key => "key1234", :owner_url => "http://google.com")
|
68
|
+
|
69
|
+
lambda {
|
70
|
+
d.audit_comment(
|
71
|
+
:user_ip => "127.0.0.1",
|
72
|
+
:article_date => Time.now,
|
73
|
+
:comment_author => "Henrik Hodne",
|
74
|
+
:comment_type => "comment",
|
75
|
+
:test_force => "ham,0.1000"
|
76
|
+
)
|
77
|
+
}.should raise_error(StandardError, "Authentication failed. Please verify your key/owner-url combination.")
|
78
|
+
end
|
79
|
+
end
|
data/spec/spec.opts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--color
|
data/spec/spec_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: defender
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Henrik Hodne
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-11-08 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rspec
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.9
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: yard
|
27
|
+
type: :development
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: "0"
|
34
|
+
version:
|
35
|
+
description: A wrapper of the Defensio spam filtering service.
|
36
|
+
email: henrik.hodne@binaryhex.com
|
37
|
+
executables: []
|
38
|
+
|
39
|
+
extensions: []
|
40
|
+
|
41
|
+
extra_rdoc_files:
|
42
|
+
- LICENSE
|
43
|
+
- README.rdoc
|
44
|
+
files:
|
45
|
+
- .document
|
46
|
+
- .gitignore
|
47
|
+
- LICENSE
|
48
|
+
- README.rdoc
|
49
|
+
- Rakefile
|
50
|
+
- VERSION
|
51
|
+
- lib/defender.rb
|
52
|
+
- spec/defender_spec.rb
|
53
|
+
- spec/spec.opts
|
54
|
+
- spec/spec_helper.rb
|
55
|
+
has_rdoc: true
|
56
|
+
homepage: http://github.com/dvyjones/defender
|
57
|
+
licenses: []
|
58
|
+
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options:
|
61
|
+
- --charset=UTF-8
|
62
|
+
require_paths:
|
63
|
+
- lib
|
64
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: "0"
|
69
|
+
version:
|
70
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
71
|
+
requirements:
|
72
|
+
- - ">="
|
73
|
+
- !ruby/object:Gem::Version
|
74
|
+
version: "0"
|
75
|
+
version:
|
76
|
+
requirements: []
|
77
|
+
|
78
|
+
rubyforge_project:
|
79
|
+
rubygems_version: 1.3.5
|
80
|
+
signing_key:
|
81
|
+
specification_version: 3
|
82
|
+
summary: Ruby API wrapper for Defensio
|
83
|
+
test_files:
|
84
|
+
- spec/spec_helper.rb
|
85
|
+
- spec/defender_spec.rb
|