ruby-akismet 0.9.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (4) hide show
  1. data/README.rdoc +38 -12
  2. data/lib/akismet.rb +101 -32
  3. data/test/akismet_test.rb +94 -25
  4. metadata +5 -5
@@ -1,9 +1,9 @@
1
- Akismet compatible library for checking spams.
1
+ Ruby library for the Akismet anti-spam service.
2
2
 
3
3
  = Usage
4
4
 
5
5
  First you need an Akismet (or Typepad Antispam) API key. Then you need
6
- to setup a few configuration variable:
6
+ to setup a few configuration variables:
7
7
 
8
8
  Akismet.key = '123456789'
9
9
  Akismet.blog = 'http://example.com'
@@ -12,37 +12,61 @@ To use Typepad Antispam, just specify the host:
12
12
 
13
13
  Akismet.host = 'api.antispam.typepad.com'
14
14
 
15
- Then you need to call any of the methods with a few attributes,
16
- and possibly an <code>ActionDispatch::Request</code> object.
15
+ Then you need to call any of the methods with a few attributes, and possibly
16
+ an ActionDispatch::Request object.
17
17
 
18
- = Documentation
18
+ == Documentation
19
+
20
+ Check rubydoc.info:
19
21
 
20
22
  http://rubydoc.info/github/ysbaddaden/ruby-akismet/master/frames
21
23
 
24
+ Or generate your own:
25
+
26
+ ~/src/ruby-akismet$ rake rdoc
27
+
22
28
  = Integrate with Ruby on Rails
23
29
 
24
- On rails 3, add this gem to your <code>Gemfile</code>:
30
+ ruby-akismet integrates nicely with Ruby on Rails, but isn't tied to it except
31
+ for the ActionDispatch::Request object, which isn't required. It should be
32
+ easily integratable with your favorite framework like Sinatra and Merb.
33
+
34
+ == Rails 3
35
+
36
+ Add this gem to your Gemfile:
25
37
 
26
38
  gem 'ruby-akismet', :require => 'akismet'
27
39
 
28
- On rails 2, first install the gem:
40
+ == Rails 2
41
+
42
+ First install the gem:
29
43
 
30
44
  gem install ruby-akismet
31
45
 
32
- Then add the gem to your app:
46
+ Then add it to your app:
33
47
 
34
48
  config.gem 'ruby-akismet', :lib => 'akismet'
35
49
 
36
- Create an initializer file like <code>config/initializers/akismet.rb</code>
50
+ == Configuration
51
+
52
+ Create an initializer file like <tt>config/initializers/akismet.rb</tt>
37
53
  with your configuration:
38
54
 
39
- Akismet.key = '123456789'
40
- Akismet.blog = 'http://example.com'
55
+ Akismet.key = '123456789'
56
+ Akismet.blog = 'http://example.com'
57
+ Akismet.logger = Rails.logger
58
+
59
+ == Usage
41
60
 
42
- Then in your controller call the appropriate methods (rails 3 example):
61
+ ruby-akismet is meant to be used on the controller side and not on the model
62
+ side, because the Akismet API requires some data that's only available from
63
+ the HTTP request --like the user and proxy IP, referer, etc.
64
+
65
+ Here is a Rails 3 example:
43
66
 
44
67
  class CommentsController < ApplicationController
45
68
  before_filter :set_post
69
+
46
70
  respond_to :html, :xml
47
71
 
48
72
  def create
@@ -84,6 +108,8 @@ Then in your controller call the appropriate methods (rails 3 example):
84
108
 
85
109
  = Author
86
110
 
111
+ - Julien Portalier <ysbaddaden@gmail.com>
112
+
87
113
  ruby-akismet is a complete rewrite of Akismetor by Ryan Bates and
88
114
  Levy Carneiro Jr. that you can find at http://github.com/levycarneiro/akismetor
89
115
 
@@ -1,46 +1,91 @@
1
1
  require 'net/http'
2
2
 
3
3
  # Akismet compatible library for checking spams.
4
+ #
5
+ # Before calling any method, you must configure a blog (your website
6
+ # homepage) and your Akismet or Typepad Antispam API key.
7
+ #
8
+ # Akismet.key = '123456789'
9
+ # Akismet.blog = 'http://example.com'
10
+ #
4
11
  class Akismet
5
- VERSION = '0.9.3'.freeze
6
- API_VERSION = '1.1'.freeze
12
+ # Raised whenever a command is issued but the API key hasn't been configured.
13
+ class MissingKey < StandardError
14
+ end
7
15
 
8
- @@host = 'rest.akismet.com'
9
- @@key = nil
10
- @@blog = nil
11
- @@extra_headers = [
12
- 'HTTP_REMOTE_ADDR',
13
- 'HTTP_CLIENT_IP',
14
- 'HTTP_X_FORWARDED_FOR',
15
- 'HTTP_CONNECTION'
16
- ]
16
+ VERSION = '1.0.0'.freeze
17
+ API_VERSION = '1.1'.freeze
17
18
 
18
19
  class << self
19
- # Configure an alternate API server.
20
+ # Configure an alternate API host server (defaults to
21
+ # <tt>'rest.akismet.com'</tt>).
20
22
  def host=(host)
21
23
  @@host = host
22
24
  end
23
25
 
26
+ def host
27
+ @@host
28
+ end
29
+
24
30
  # Configure your API key (required).
25
31
  def key=(key)
26
32
  @@key = key
27
33
  end
28
34
 
29
- # Configure your homepage URL (optional).
35
+ def key
36
+ @@key
37
+ end
38
+
39
+ # Configure your homepage URL (required).
30
40
  def blog=(blog)
31
41
  @@blog = blog
32
42
  end
33
43
 
34
- # Configure an array of extra HTTP headers to pass to Akismet from
35
- # the request.
44
+ def blog
45
+ @@blog
46
+ end
47
+
48
+ def logger=(logger)
49
+ @@logger = logger
50
+ end
51
+
52
+ def logger
53
+ @@logger
54
+ end
55
+
56
+ # Configure an Array of extra HTTP headers to pass to the Akismet server
57
+ # to extract from the request object.
58
+ #
59
+ # Defaults to:
60
+ #
61
+ # [
62
+ # 'HTTP_REMOTE_ADDR',
63
+ # 'HTTP_CLIENT_IP',
64
+ # 'HTTP_X_FORWARDED_FOR',
65
+ # 'HTTP_CONNECTION'
66
+ # ]
36
67
  #
37
- # Example:
68
+ # Examples:
69
+ #
70
+ # # replaces the actual list:
71
+ # Akismet.extra_headers = ['HTTP_REMOTE_ADDR']
72
+ #
73
+ # # appends a header to the list:
74
+ # Akismet.extra_headers << 'HTTP_ACCEPT_CHARSET'
75
+ #
76
+ # # appends multiple headers to the list:
77
+ # Akismet.extra_headers << ['HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_LANGUAGE']
38
78
  #
39
- # Akismet.extra_headers = ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR']
40
79
  def extra_headers=(headers)
41
80
  @@extra_headers = headers
42
81
  end
43
82
 
83
+ def extra_headers
84
+ @@extra_headers.flatten!
85
+ @@extra_headers.uniq!
86
+ @@extra_headers
87
+ end
88
+
44
89
  # Checks if a key is valid or not.
45
90
  def valid_key?(key)
46
91
  call('verify-key', :key => key) == "valid"
@@ -50,18 +95,25 @@ class Akismet
50
95
  #
51
96
  # Required attributes:
52
97
  #
53
- # - <code>:permalink</code>
54
- # - <code>:comment_author</code>
55
- # - <code>:comment_author_url</code>
56
- # - <code>:comment_author_email</code>
57
- # - <code>:comment_content</code>
98
+ # - <tt>:permalink</tt>
99
+ # - <tt>:comment_author</tt>
100
+ # - <tt>:comment_author_url</tt>
101
+ # - <tt>:comment_author_email</tt>
102
+ # - <tt>:comment_content</tt>
103
+ #
104
+ # Those are also required, but will be extracted from the
105
+ # +request+ object if available:
58
106
  #
59
- # Those are also required, but will be extracted from the request object:
107
+ # - <tt>:user_ip</tt>
108
+ # - <tt>:user_agent</tt>
109
+ # - <tt>:referrer</tt> (check spelling!)
60
110
  #
61
- # - <code>:user_ip</code>
62
- # - <code>:user_agent</code>
63
- # - <code>:referer</code>
64
- # - plus any more relevant HTTP header.
111
+ # Plus more relevant HTTP headers from extra_headers.
112
+ #
113
+ # Note that request is supposed to be an instance of
114
+ # ActionDispatch::Request or ActionController::Request. If not, the object
115
+ # must respond to +remote_ip+ (IP as string) and +headers+
116
+ # (an array of HTTP headers).
65
117
  #
66
118
  def spam?(attributes, request = nil)
67
119
  call('comment-check', attributes, request) == "true"
@@ -79,7 +131,7 @@ class Akismet
79
131
  end
80
132
 
81
133
  # Submits a false-positive comment as non-spam to Akismet.
82
- # Takes the same attributes than +spam+.
134
+ # Takes the same attributes than spam?.
83
135
  def submit_ham(attributes)
84
136
  call('submit-ham', attributes)
85
137
  end
@@ -90,6 +142,17 @@ class Akismet
90
142
  end
91
143
  end
92
144
 
145
+ self.host = 'rest.akismet.com'
146
+ self.key = nil
147
+ self.blog = nil
148
+ self.logger = nil
149
+ self.extra_headers = [
150
+ 'HTTP_REMOTE_ADDR',
151
+ 'HTTP_CLIENT_IP',
152
+ 'HTTP_X_FORWARDED_FOR',
153
+ 'HTTP_CONNECTION'
154
+ ]
155
+
93
156
  def initialize(command, attributes, request = nil)
94
157
  @command = command
95
158
  @attributes = attributes
@@ -97,13 +160,15 @@ class Akismet
97
160
  end
98
161
 
99
162
  def call
163
+ self.class.logger.debug { " AKISMET #{@command} #{post_attributes}" } if self.class.logger
164
+
100
165
  http = Net::HTTP.new(http_host, 80)
101
166
  http.post(http_path, post_attributes, http_headers).body
102
167
  end
103
168
 
104
169
  private
105
170
  def attributes
106
- @attributes[:blog] ||= @@blog
171
+ @attributes[:blog] ||= self.class.blog
107
172
 
108
173
  unless @command == 'verify-key'
109
174
  @attributes[:comment_type] ||= 'comment'
@@ -112,7 +177,10 @@ class Akismet
112
177
  @attributes[:user_ip] = @request.remote_ip
113
178
  @attributes[:user_agent] = @request.headers["HTTP_USER_AGENT"]
114
179
  @attributes[:referrer] = @request.headers["HTTP_REFERER"]
115
- @@extra_headers.each { |h| @attributes[h] = @request.headers[h] }
180
+
181
+ self.class.extra_headers.each do |h|
182
+ @attributes[h] = @request.headers[h]
183
+ end
116
184
  end
117
185
  end
118
186
 
@@ -133,9 +201,10 @@ class Akismet
133
201
 
134
202
  def http_host
135
203
  unless @command == 'verify-key'
136
- "#{@@key}.#{@@host}"
204
+ raise MissingKey.new("Required Akismet.key is nil.") unless self.class.key
205
+ "#{self.class.key}.#{self.class.host}"
137
206
  else
138
- "#{@@host}"
207
+ "#{self.class.host}"
139
208
  end
140
209
  end
141
210
 
@@ -1,60 +1,129 @@
1
1
  require 'test/unit'
2
+ require 'logger'
3
+
4
+ require 'rubygems'
5
+ gem 'actionpack'
6
+ gem 'rack'
7
+ require 'action_dispatch'
8
+
2
9
  require File.expand_path("../../lib/akismet.rb", __FILE__)
3
10
 
4
- # TODO: Test with an ActionDispatch::TestRequest object.
5
11
  class AkismetTest < Test::Unit::TestCase
6
12
  def setup
7
13
  Akismet.host = 'api.antispam.typepad.com'
8
14
  Akismet.key = '123456789'
9
15
  Akismet.blog = 'http://www.example.com/'
10
- end
11
-
12
- def valid_attributes
13
- {
14
- :user_ip => '127.0.0.1',
15
- :user_agent => 'Mozilla/5.0 (Macintosh; U; PPC Mac OS X 10.5; en-US; rv:1.9.0.3) Gecko/2008092414 Firefox/3.0.3',
16
- :referrer => 'http://www.example.com/posts',
17
- :permalink => 'http://www.example.com/posts/1',
18
- :comment_author => 'Julien Portalier',
19
- :comment_author_url => 'http://ysbaddaden.wordpress.com/',
20
- :comment_author_email => 'julien@example.com',
21
- :comment_content => 'this is a normal comment',
22
- }
23
- end
24
-
25
- def invalid_attributes
26
- invalid = valid_attributes.dup
27
- invalid[:comment_author] = 'viagra-test-123'
28
- invalid
16
+
17
+ Akismet.logger = Logger.new(File.expand_path("../test.log", __FILE__))
18
+ Akismet.logger.level = Logger::DEBUG
29
19
  end
30
20
 
31
21
  def test_valid_key
32
- assert Akismet.valid_key?('123456789')
22
+ assert Akismet.valid_key?(Akismet.key)
33
23
  end
34
24
 
35
25
  # def test_invalid_key
36
26
  # assert !Akismet.valid_key?('abc123')
37
27
  # end
38
28
 
29
+ def test_should_not_fail_with_no_logger
30
+ Akismet.logger = nil
31
+ assert Akismet.valid_key?(Akismet.key)
32
+ end
33
+
34
+ def test_should_raise_missing_key
35
+ Akismet.key = nil
36
+ assert_raise(Akismet::MissingKey) { Akismet.spam?(full_spam_attributes) }
37
+ end
38
+
39
+ def test_should_not_raise_missing_key_for_valid_key
40
+ Akismet.key = nil
41
+ assert_nothing_raised { Akismet.valid_key?('123456789') }
42
+ end
43
+
44
+ def test_extra_headers
45
+ Akismet.extra_headers = ['HTTP_REMOTE_ADDR']
46
+ assert_equal ['HTTP_REMOTE_ADDR'], Akismet.extra_headers
47
+
48
+ Akismet.extra_headers << 'HTTP_CLIENT_IP'
49
+ assert_equal ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP'], Akismet.extra_headers
50
+
51
+ Akismet.extra_headers << ['HTTP_X_FORWARDED_FOR', 'HTTP_CONNECTION']
52
+ assert_equal ['HTTP_REMOTE_ADDR', 'HTTP_CLIENT_IP', 'HTTP_X_FORWARDED_FOR', 'HTTP_CONNECTION'],
53
+ Akismet.extra_headers
54
+ end
55
+
56
+ def test_spam_with_actiondispatch_request
57
+ assert Akismet.spam?(spam_attributes, actiondispatch_request)
58
+ end
59
+
60
+ def test_ham_with_actiondispatch_request
61
+ assert !Akismet.spam?(ham_attributes, actiondispatch_request)
62
+ end
63
+
39
64
  def test_spam
40
- assert Akismet.spam?(invalid_attributes)
65
+ assert Akismet.spam?(full_spam_attributes)
41
66
  end
42
67
 
43
68
  def test_not_spam
44
- assert !Akismet.spam?(valid_attributes)
69
+ assert !Akismet.spam?(full_ham_attributes)
45
70
  end
46
71
 
47
72
  def test_ham
48
- assert Akismet.ham?(valid_attributes)
73
+ assert Akismet.ham?(full_ham_attributes)
49
74
  end
50
75
 
51
76
  def test_not_ham
52
- assert !Akismet.ham?(invalid_attributes)
77
+ assert !Akismet.ham?(full_spam_attributes)
53
78
  end
54
79
 
55
80
  def test_submit_spam
81
+ assert Akismet.submit_spam(spam_attributes)
56
82
  end
57
83
 
58
84
  def test_submit_ham
85
+ assert Akismet.submit_ham(ham_attributes)
59
86
  end
87
+
88
+ protected
89
+ def ham_attributes
90
+ {
91
+ :permalink => 'http://www.example.com/posts/1',
92
+ :comment_author => 'Julien Portalier',
93
+ :comment_author_url => 'http://ysbaddaden.wordpress.com/',
94
+ :comment_author_email => 'julien@example.com',
95
+ :comment_content => 'this is a normal comment'
96
+ }
97
+ end
98
+
99
+ def spam_attributes
100
+ ham_attributes.merge(:comment_author => 'viagra-test-123')
101
+ end
102
+
103
+ def additional_attributes
104
+ {
105
+ :user_ip => '127.0.0.1',
106
+ :user_agent => 'Mozilla/5.0 (X11; U; Linux i686; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Ubuntu/10.04 Chromium/7.0.517.41 Chrome/7.0.517.41 Safari/534.7',
107
+ :referrer => 'http://www.example.com/posts'
108
+ }
109
+ end
110
+
111
+ def full_spam_attributes
112
+ spam_attributes.merge(additional_attributes)
113
+ end
114
+
115
+ def full_ham_attributes
116
+ ham_attributes.merge(additional_attributes)
117
+ end
118
+
119
+ def actiondispatch_request
120
+ request = ActionDispatch::TestRequest.new(
121
+ 'HTTP_REFERER' => 'http://www.example.com/posts/1',
122
+ 'HTTP_REMOTE_ADDR' => '127.0.0.1',
123
+ 'HTTP_USER_AGENT' => 'Mozilla/5.0 (X11; U; Linux i686; fr-FR) AppleWebKit/534.7 (KHTML, like Gecko) Ubuntu/10.04 Chromium/7.0.517.41 Chrome/7.0.517.41 Safari/534.7',
124
+ 'HTTP_CONNECTION' => 'close'
125
+ )
126
+ request.remote_addr = '127.0.0.1'
127
+ request
128
+ end
60
129
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-akismet
3
3
  version: !ruby/object:Gem::Version
4
- hash: 61
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
+ - 1
7
8
  - 0
8
- - 9
9
- - 3
10
- version: 0.9.3
9
+ - 0
10
+ version: 1.0.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Julien Portalier
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-10-23 00:00:00 +02:00
18
+ date: 2010-10-24 00:00:00 +02:00
19
19
  default_executable:
20
20
  dependencies: []
21
21