defender 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,12 +2,6 @@
2
2
 
3
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
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
5
  == Note on Patches/Pull Requests
12
6
 
13
7
  * Fork the project.
data/Rakefile CHANGED
@@ -12,6 +12,7 @@ begin
12
12
  gem.authors = ["Henrik Hodne"]
13
13
  gem.add_development_dependency "rspec", ">= 1.2.9"
14
14
  gem.add_development_dependency "yard", ">= 0"
15
+ gem.add_development_dependency "mocha", ">= 0.9.8"
15
16
  # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
16
17
  end
17
18
  Jeweler::GemcutterTasks.new
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.0
1
+ 0.1.1
@@ -5,10 +5,12 @@ class Defender
5
5
  # The Defensio API version currently supported by Defender
6
6
  API_VERSION = "1.2"
7
7
 
8
+ ROOT_URL = "http://api.defensio.com/"
9
+
8
10
  DEFAULT_OPTIONS = {
9
- service_type: "blog",
10
- api_key: "",
11
- owner_url: ""
11
+ :service_type => "blog",
12
+ :api_key => "",
13
+ :owner_url => ""
12
14
  }
13
15
 
14
16
  ##
@@ -48,13 +50,9 @@ class Defender
48
50
  # otherwise.
49
51
  #
50
52
  # @return [Boolean]
51
- def spam?
52
- @spam
53
- end
53
+ def spam?; @spam; end
54
54
 
55
- def to_s
56
- @signature
57
- end
55
+ def to_s; @signature; end
58
56
  end
59
57
 
60
58
  ##
@@ -66,47 +64,39 @@ class Defender
66
64
  # Defensio on this blog.
67
65
  #
68
66
  # @return [Float<0..1>]
69
- attr_reader :accuracy
67
+ def accuracy; @response["accuracy"]; end
70
68
 
71
69
  ##
72
70
  # The number of spam comments caught by the filter.
73
- attr_reader :spam
71
+ def spam; @response["spam"]; end
74
72
 
75
73
  ##
76
74
  # The number of ham (legitimate) comments accepted by the filter.
77
- attr_reader :ham
75
+ def ham; @response["ham"]; end
78
76
 
79
77
  ##
80
78
  # The number of times a legitimate message was retrained from the spambox
81
79
  # (i.e. "de-spammed" by the user)
82
- attr_reader :false_positives
80
+ def false_positives; @response["false-positives"]; end
83
81
 
84
82
  ##
85
83
  # The number of times a spam message was retrained from comments box (i.e.
86
84
  # "de-legitimized" by the user)
87
- attr_reader :false_negatives
85
+ def false_negatives; @response["false-negatives"]; end
88
86
 
89
87
  ##
90
88
  # A boolean value indicating whether Defensio is still in its initial
91
89
  # learning phase.
92
90
  #
93
91
  # @return [Boolean]
94
- attr_reader :learning
92
+ def learning; @response["learning"]; end
95
93
 
96
94
  ##
97
95
  # More details on the reason(s) why Defensio is still in its initial
98
96
  # learning phase.
99
- attr_reader :learning_status
97
+ def learning_status; @response["learning-status"]; end
100
98
 
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
99
+ def initialize(response); @response = response; end
110
100
  end
111
101
 
112
102
  attr_accessor :service_type, :api_key, :owner_url
@@ -131,11 +121,9 @@ class Defender
131
121
  # @return [Hash]
132
122
  def self.options_to_parameters(options)
133
123
  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
124
+ options.each do |key, val|
125
+ opts[key.to_s.gsub("_", "-").downcase] = val.respond_to?(:strftime) ?
126
+ val.strftime("%Y/%m/%d") : val.to_s
139
127
  end
140
128
  opts
141
129
  end
@@ -162,40 +150,27 @@ class Defender
162
150
  # @return [Boolean]
163
151
  # @see http://defensio.com/api/#validate-key
164
152
  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
153
+ call_action("validate-key")["status"] == "success" ? true : false
175
154
  end
176
155
 
177
156
  ##
178
157
  # Announce an article existence. This should (if feasible) be called when an
179
158
  # article or blogpost is created so Defensio can analyse it.
180
159
  #
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
160
+ # @param [Hash] opts All options are required.
161
+ # @option opts [#to_s] :article_title The title of the article
162
+ # @option opts [#to_s] :article_author The name of the author of the article
163
+ # @option opts [#to_s] :article_author_email The email address of the person posting the
184
164
  # article.
185
- # @param [#to_s] content The content of the article itself.
186
- # @param [#to_s] permalink The permalink of the article just posted.
165
+ # @option opts [#to_s] :article_content The content of the article itself.
166
+ # @option opts [#to_s] :permalink The permalink of the article just posted.
187
167
  # @raise [StandardError] If the call fails, a StandardError is raised with
188
168
  # the error message given from Defensio.
189
169
  # @return [Boolean] Returns true if the article was successfully announced,
190
170
  # raises StandardError otherwise.
191
171
  # @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)
172
+ def announce_article(opts={})
173
+ response = call_action(Defender.options_to_parameters(opts))
199
174
  true
200
175
  end
201
176
 
@@ -258,9 +233,7 @@ class Defender
258
233
  # @return [Boolean] Returns true if the comments were successfully marked,
259
234
  # raises StandardError otherwise.
260
235
  def report_false_negatives(signatures)
261
- response = call_action("report-false-negatives",
262
- "signatures" => signatures.map(&:to_s).join(","))
263
- true
236
+ report_false(:negatives, signatures)
264
237
  end
265
238
 
266
239
  ##
@@ -277,9 +250,7 @@ class Defender
277
250
  # @return [Boolean] Returns true if the comments were successfully marked,
278
251
  # raises StandardError otherwise.
279
252
  def report_false_positives(signatures)
280
- response = call_action("report-false-positives",
281
- "signatures" => signatures.map(&:to_s).join(","))
282
- true
253
+ report_false(:positives, signatures)
283
254
  end
284
255
 
285
256
  ##
@@ -293,6 +264,12 @@ class Defender
293
264
  end
294
265
 
295
266
  private
267
+ def report_false(type, signatures)
268
+ call_action("report-false-#{type}",
269
+ "signatures" => signatures.join(","))
270
+ true
271
+ end
272
+
296
273
  ##
297
274
  # Returns the url for the given action.
298
275
  #
@@ -301,11 +278,7 @@ class Defender
301
278
  # @raise [APIKeyError] Raises this if no API key is given.
302
279
  def url(action)
303
280
  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"
281
+ "#{ROOT_URL}#{@service_type}/#{API_VERSION}/#{action}/#{@api_key}.yaml"
309
282
  end
310
283
 
311
284
  ##
@@ -317,12 +290,10 @@ class Defender
317
290
  # @raise [APIKeyError] If an invalid (or no) API key is given, this is
318
291
  # raised
319
292
  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
293
+ response = Net::HTTP.post_form(URI.parse(url(action)),
294
+ {"owner-url" => @owner_url}.merge(params))
295
+ response.code == 401 ?
296
+ raise(APIKeyError) :
325
297
  Defender.raise_if_error(YAML.load(response.body)["defensio-result"])
326
- end
327
298
  end
328
299
  end
@@ -1,6 +1,10 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "Defender" do
4
+ before(:each) do
5
+ @defender = Defender.new(:api_key => "validkey", :owner_url => "validurl")
6
+ end
7
+
4
8
  it "should raise a StandardError if a method fails" do
5
9
  lambda do
6
10
  Defender.raise_if_error({
@@ -11,26 +15,39 @@ describe "Defender" do
11
15
  end
12
16
 
13
17
  it "should return the correct URL for any given action" do
14
- d = Defender.new(:api_key => "key1234")
15
- d.instance_eval do
18
+ @defender.instance_eval do
16
19
  url("foobar")
17
- end.should == "http://api.defensio.com/blog/#{Defender::API_VERSION}/foobar/key1234.yaml"
20
+ end.should == "http://api.defensio.com/blog/#{Defender::API_VERSION}/foobar/validkey.yaml"
18
21
  end
19
22
 
20
23
  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
24
+ @defender.stubs(:call_action).with("validate-key").returns(
25
+ {"status" => "success", "message" => ""}
26
+ )
27
+ @defender.valid_key?.should be_true
23
28
  end
24
29
 
25
30
  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
31
+ @defender.stubs(:call_action).with("validate-key").returns(
32
+ {"status" => "fail", "message" => "Invalid key"}
33
+ )
34
+ @defender.valid_key?.should be_false
28
35
  end
29
36
 
30
37
  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(
38
+ @defender.
39
+ stubs(:call_action).
40
+ with('audit-comment', {
41
+ "user-ip" => "127.0.0.1",
42
+ "article-date" => Time.now.strftime("%Y/%m/%d"),
43
+ "comment-author" => "Henrik Hodne",
44
+ "comment-type" => "comment",
45
+ "test-force" => "spam,0.5000",
46
+ }).
47
+ returns(
48
+ {"signature" => "abc123", "spam" => true, "spaminess" => 0.5}
49
+ )
50
+ @defender.audit_comment(
34
51
  :user_ip => "127.0.0.1",
35
52
  :article_date => Time.now,
36
53
  :comment_author => "Henrik Hodne",
@@ -40,9 +57,19 @@ describe "Defender" do
40
57
  end
41
58
 
42
59
  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(
60
+ @defender.
61
+ stubs(:call_action).
62
+ with('audit-comment', {
63
+ "user-ip" => "127.0.0.1",
64
+ "article-date" => Time.now.strftime("%Y/%m/%d"),
65
+ "comment-author" => "Henrik Hodne",
66
+ "comment-type" => "comment",
67
+ "test-force" => "ham,0.1000",
68
+ }).
69
+ returns(
70
+ {"signature" => "abc123", "spam" => false, "spaminess" => 0.1}
71
+ )
72
+ @defender.audit_comment(
46
73
  :user_ip => "127.0.0.1",
47
74
  :article_date => Time.now,
48
75
  :comment_author => "Henrik Hodne",
@@ -52,9 +79,19 @@ describe "Defender" do
52
79
  end
53
80
 
54
81
  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(
82
+ @defender.
83
+ stubs(:call_action).
84
+ with('audit-comment', {
85
+ "user-ip" => "127.0.0.1",
86
+ "article-date" => Time.now.strftime("%Y/%m/%d"),
87
+ "comment-author" => "Henrik Hodne",
88
+ "comment-type" => "comment",
89
+ "test-force" => "spam,0.5000",
90
+ }).
91
+ returns(
92
+ {"signature" => "abc123", "spam" => true, "spaminess" => 0.5}
93
+ )
94
+ @defender.audit_comment(
58
95
  :user_ip => "127.0.0.1",
59
96
  :article_date => Time.now,
60
97
  :comment_author => "Henrik Hodne",
@@ -64,8 +101,16 @@ describe "Defender" do
64
101
  end
65
102
 
66
103
  it "should fail without valid API credentials" do
67
- d = Defender.new(:api_key => "key1234", :owner_url => "http://google.com")
68
-
104
+ @defender.
105
+ stubs(:call_action).
106
+ with('audit-comment', {
107
+ "user-ip" => "127.0.0.1",
108
+ "article-date" => Time.now.strftime("%Y/%m/%d"),
109
+ "comment-author" => "Henrik Hodne",
110
+ "comment-type" => "comment",
111
+ "test-force" => "ham,0.1000",
112
+ }).
113
+ raises(StandardError)
69
114
  lambda {
70
115
  d.audit_comment(
71
116
  :user_ip => "127.0.0.1",
@@ -74,6 +119,6 @@ describe "Defender" do
74
119
  :comment_type => "comment",
75
120
  :test_force => "ham,0.1000"
76
121
  )
77
- }.should raise_error(StandardError, "Authentication failed. Please verify your key/owner-url combination.")
122
+ }.should raise_error(StandardError)
78
123
  end
79
124
  end
@@ -3,6 +3,7 @@ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
3
  require 'defender'
4
4
  require 'spec'
5
5
  require 'spec/autorun'
6
+ require 'mocha'
6
7
 
7
8
  Spec::Runner.configure do |config|
8
9
 
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: defender
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henrik Hodne
@@ -9,7 +9,7 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2009-11-08 00:00:00 +01:00
12
+ date: 2009-11-09 00:00:00 +01:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
@@ -32,6 +32,16 @@ dependencies:
32
32
  - !ruby/object:Gem::Version
33
33
  version: "0"
34
34
  version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: mocha
37
+ type: :development
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.9.8
44
+ version:
35
45
  description: A wrapper of the Defensio spam filtering service.
36
46
  email: henrik.hodne@binaryhex.com
37
47
  executables: []