defender 0.1.0 → 0.1.1

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.
@@ -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: []