Commit dfaacfd5899befe7e83995bc3388bf8ee6f71d09
1 parent
53dfdbc8
Exists in
master
and in
29 other branches
Adding rakismet 1.3.0
Showing
21 changed files
with
917 additions
and
0 deletions
Show diff stats
@@ -0,0 +1,35 @@ | @@ -0,0 +1,35 @@ | ||
1 | +* Clean up gemspec and load paths [Steven Harman] | ||
2 | +* Add Akismet is_test param [Steven Harman] | ||
3 | +* Add Akismet user_role attribute [Steven Harman] | ||
4 | += 1.2.1 | ||
5 | +* Fix deprecated usage of HTTPResponse for Ruby 1.9.3 [Leonid Shevtsov] | ||
6 | += 1.2.0 | ||
7 | +* Rakismet attribute mappings are now inheritable | ||
8 | += 1.1.2 | ||
9 | +* Explicitly load version | ||
10 | += 1.1.1 | ||
11 | +* Fix SafeBuffer error under Rails 3.0.8 and 3.0.9 [Brandon Ferguson] | ||
12 | +* Readme cleanup [Zeke Sikelianos] | ||
13 | +* Drop Jeweler in favor of Bundler's gem tasks | ||
14 | += 1.1.0 | ||
15 | +* Add HTTP Proxy support [Francisco Trindade] | ||
16 | += 1.0.1 | ||
17 | +* Fix hash access for Ruby 1.9 [Alex Crichton] | ||
18 | += 1.0.0 | ||
19 | +* Update for Rails 3 | ||
20 | +* Remove filters and replace with middleware | ||
21 | +* Remove initializers and replace with Railtie | ||
22 | += 0.4.0 | ||
23 | +* Rakismet is no longer injected into ActiveRecord or ActionController | ||
24 | +* API changes to support newly decoupled modules | ||
25 | +* Use Jeweler to manage gemspec | ||
26 | += 0.3.6 | ||
27 | +* Allow attributes to fall through to methods or AR attributes | ||
28 | += 0.3.5 | ||
29 | +* Added gemspec and rails/init.rb so rakismet can work as a gem [Michael Air] | ||
30 | +* Added generator template and manifest [Michael Air] | ||
31 | += 0.3.0 | ||
32 | +* Abstract out Rakismet version string | ||
33 | +* Set default Akismet Host | ||
34 | +* Abstract out the Akismet host [Mike Burns] | ||
35 | +* Started keeping a changelog :P |
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +Copyright (c) 2008 Josh French | ||
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. |
@@ -0,0 +1,229 @@ | @@ -0,0 +1,229 @@ | ||
1 | +Rakismet | ||
2 | +======== | ||
3 | + | ||
4 | +**Akismet** (<http://akismet.com/>) is a collaborative spam filtering service. | ||
5 | +**Rakismet** is easy Akismet integration with Rails and rack apps. TypePad's | ||
6 | +AntiSpam service and generic Akismet endpoints are supported. | ||
7 | + | ||
8 | +Compatibility | ||
9 | +============= | ||
10 | + | ||
11 | +**Rakismet >= 1.0.0** work with Rails 3 and other Rack-based frameworks. | ||
12 | + | ||
13 | +**Rakismet <= 0.4.2** is compatible with Rails 2. | ||
14 | + | ||
15 | +Getting Started | ||
16 | +=============== | ||
17 | + | ||
18 | +Once you've installed the Rakismet gem and added it to your application's Gemfile, | ||
19 | +you'll need an API key. Head on over to http://akismet.com/signup/ and sign up | ||
20 | +for a new username. | ||
21 | + | ||
22 | +Configure the Rakismet key and the URL of your application by setting the following | ||
23 | +in application.rb: | ||
24 | + | ||
25 | +```ruby | ||
26 | +config.rakismet.key = 'your wordpress key' | ||
27 | +config.rakismet.url = 'http://yourdomain.com/' | ||
28 | +``` | ||
29 | + | ||
30 | +or an initializer, for example `config/initializers/rakismet.rb`: | ||
31 | + | ||
32 | +```ruby | ||
33 | +YourApp::Application.config.rakismet.key = 'your wordpress key' | ||
34 | +YourApp::Application.config.rakismet.url = 'http://yourdomain.com/' | ||
35 | +``` | ||
36 | + | ||
37 | +If you wish to use another Akismet-compatible API provider such as TypePad's | ||
38 | +antispam service, you'll also need to set `config.rakismet.host` to your service | ||
39 | +provider's endpoint. | ||
40 | + | ||
41 | +If you want to use a proxy to access akismet (i.e. your application is behind a | ||
42 | +firewall), set the proxy_host and proxy_port option. | ||
43 | + | ||
44 | +```ruby | ||
45 | +config.rakismet.proxy_host = 'http://yourdomain.com/' | ||
46 | +config.rakismet.proxy_port = '8080' | ||
47 | +``` | ||
48 | + | ||
49 | +Checking For Spam | ||
50 | +----------------- | ||
51 | + | ||
52 | +First, introduce Rakismet to your model: | ||
53 | + | ||
54 | +```ruby | ||
55 | +class Comment | ||
56 | + include Rakismet::Model | ||
57 | +end | ||
58 | +``` | ||
59 | + | ||
60 | +With Rakismet mixed in to your model, you'll get three instance methods for interacting with | ||
61 | +Akismet: | ||
62 | + | ||
63 | + * `spam?` submits the comment to Akismet and returns true if Akismet thinks the comment is spam, false if not. | ||
64 | + * `ham!` resubmits a valid comment that Akismet erroneously marked as spam (marks it as a false positive.) | ||
65 | + * `spam!` resubmits a spammy comment that Akismet missed (marks it as a false negative.) | ||
66 | + | ||
67 | +The `ham!` and `spam!` methods will change the value of `spam?` but their | ||
68 | +primary purpose is to send feedback to Akismet. The service works best when you | ||
69 | +help correct the rare mistake; please consider using these methods if you're | ||
70 | +moderating comments or otherwise reviewing the Akismet responses. | ||
71 | + | ||
72 | +Configuring Your Model | ||
73 | +---------------------- | ||
74 | + | ||
75 | +Rakismet sends the following information to the spam-hungry robots at Akismet: | ||
76 | + | ||
77 | + author : name submitted with the comment | ||
78 | + author_url : URL submitted with the comment | ||
79 | + author_email : email submitted with the comment | ||
80 | + comment_type : Defaults to comment but you can set it to trackback, pingback, or something more appropriate | ||
81 | + content : the content submitted | ||
82 | + permalink : the permanent URL for the entry the comment belongs to | ||
83 | + user_ip : IP address used to submit this comment | ||
84 | + user_agent : user agent string | ||
85 | + referrer : referring URL (note the spelling) | ||
86 | + | ||
87 | +By default, Rakismet just looks for attributes or methods on your class that | ||
88 | +match these names. You don't have to have accessors that match these exactly, | ||
89 | +however. If yours differ, just tell Rakismet what to call them: | ||
90 | + | ||
91 | +```ruby | ||
92 | +class Comment | ||
93 | + include Rakismet::Model | ||
94 | + attr_accessor :commenter_name, :commenter_email | ||
95 | + rakismet_attrs :author => :commenter_name, :author_email => :commenter_email | ||
96 | +end | ||
97 | +``` | ||
98 | + | ||
99 | +Or you can pass in a proc, to access associations: | ||
100 | + | ||
101 | +```ruby | ||
102 | +class Comment < ActiveRecord::Base | ||
103 | + include Rakismet::Model | ||
104 | + belongs_to :author | ||
105 | + rakismet_attrs :author => proc { author.name }, | ||
106 | + :author_email => proc { author.email } | ||
107 | +end | ||
108 | +``` | ||
109 | + | ||
110 | +You can even hard-code specific fields: | ||
111 | + | ||
112 | +```ruby | ||
113 | +class Trackback | ||
114 | + include Rakismet::Model | ||
115 | + rakismet_attrs :comment_type => "trackback" | ||
116 | +end | ||
117 | +``` | ||
118 | + | ||
119 | +Optional Request Variables | ||
120 | +-------------------------- | ||
121 | + | ||
122 | +Akismet wants certain information about the request environment: remote IP, the | ||
123 | +user agent string, and the HTTP referer when available. Normally, Rakismet | ||
124 | +asks your model for these. Storing this information on your model allows you to | ||
125 | +call the `spam?` method at a later time. For instance, maybe you're storing your | ||
126 | +comments in an administrative queue or processing them with a background job. | ||
127 | + | ||
128 | +You don't need to have these three attributes on your model, however. If you | ||
129 | +choose to omit them, Rakismet will instead look at the current request (if one | ||
130 | +exists) and take the values from the request object instead. | ||
131 | + | ||
132 | +This means that if you are **not storing the request variables**, you must call | ||
133 | +`spam?` from within the controller action that handles comment submissions. That | ||
134 | +way the IP, user agent, and referer will belong to the person submitting the | ||
135 | +comment. If you're not storing the request variables and you call `spam?` at a | ||
136 | +later time, the request information will be missing or invalid and Akismet won't | ||
137 | +be able to do its job properly. | ||
138 | + | ||
139 | +If you've decided to handle the request variables yourself, you can disable the | ||
140 | +middleware responsible for tracking the request information by adding this to | ||
141 | +your app initialization: | ||
142 | + | ||
143 | +```ruby | ||
144 | +config.rakismet.use_middleware = false | ||
145 | +``` | ||
146 | + | ||
147 | +Testing | ||
148 | +------- | ||
149 | + | ||
150 | +Rakismet can be configued to tell Akismet that it should operate in test mode - | ||
151 | +so Akismet will not change its behavior based on any test API calls, meaning | ||
152 | +they will have no training effect. That means your tests can be somewhat | ||
153 | +repeatable in the sense that one test won't influence subsequent calls. | ||
154 | + | ||
155 | +You can configure Rakismet for test mode via application.rb: | ||
156 | + | ||
157 | +```ruby | ||
158 | +config.rakismet.test = false # <- default | ||
159 | +config.rakismet.test = true | ||
160 | +``` | ||
161 | + | ||
162 | +Or via an initializer: | ||
163 | + | ||
164 | +```ruby | ||
165 | +YourApp::Application.config.rakismet.test = false # <- default | ||
166 | +YourApp::Application.config.rakismet.test = true | ||
167 | +``` | ||
168 | + | ||
169 | +**NOTE**: When running in Rails, Rakismet will run in test mode when your Rails | ||
170 | +environment is `test` or `development`, unless explictly configured otherwise. | ||
171 | +Outside of Rails Rakismet defaults to test mode turned **off**. | ||
172 | + | ||
173 | + | ||
174 | +Verifying Responses | ||
175 | +------------------- | ||
176 | + | ||
177 | +If you want to see what's happening behind the scenes, after you call one of | ||
178 | +`@comment.spam?`, `@comment.spam!` or `@comment.ham!` you can check | ||
179 | +`@comment.akismet_response`. | ||
180 | + | ||
181 | +This will contain the last response from the Akismet server. In the case of | ||
182 | +`spam?` it should be `true` or `false.` For `spam!` and `ham!` it should be | ||
183 | +`Feedback received.` If Akismet returned an error instead (e.g. if you left out | ||
184 | +some required information) this will contain the error message. | ||
185 | + | ||
186 | +FAQ | ||
187 | +=== | ||
188 | + | ||
189 | +Why does Akismet think all of my test data is spam? | ||
190 | +--------------------------------------------------- | ||
191 | + | ||
192 | +Akismet needs enough information to decide if your test data is spam or not. | ||
193 | +Try to supply as much as possible, especially the author name and request | ||
194 | +variables. | ||
195 | + | ||
196 | +How can I simulate a spam submission? | ||
197 | +------------------------------------- | ||
198 | + | ||
199 | +Most people have the opposite problem, where Akismet doesn't think anything is | ||
200 | +spam. The only guaranteed way to trigger a positive spam response is to set the | ||
201 | +comment author to "viagra-test-123". | ||
202 | + | ||
203 | +If you've done this and `spam?` is still returning false, you're probably | ||
204 | +missing the user IP or one of the key/url config variables. One way to check is | ||
205 | +to call `@comment.akismet_response`. If you are missing a required field or | ||
206 | +there was another error, this will hold the Akismet error message. If your comment | ||
207 | +was processed normally, this value will simply be `true` or `false`. | ||
208 | + | ||
209 | +Can I use Rakismet with a different ORM or framework? | ||
210 | +----------------------------------------------------- | ||
211 | + | ||
212 | +Sure. Rakismet doesn't care what your persistence layer is. It will work with | ||
213 | +Datamapper, a NoSQL store, or whatever next month's DB flavor is. | ||
214 | + | ||
215 | +Rakismet also has no dependencies on Rails or any of its components, and only | ||
216 | +uses a small Rack middleware object to do some of its magic. Depending on your | ||
217 | +framework, you may have to modify this slightly and/or manually place it in your | ||
218 | +stack. | ||
219 | + | ||
220 | +You'll also need to set a few config variables by hand. Instead of | ||
221 | +`config.rakismet.key`, `config.rakismet.url`, and `config.rakismet.host`, set | ||
222 | +these values directly with `Rakismet.key`, `Rakismet.url`, and `Rakismet.host`. | ||
223 | + | ||
224 | +--------------------------------------------------------------------------- | ||
225 | + | ||
226 | +If you have any implementation or usage questions, don't hesitate to get in | ||
227 | +touch: josh@vitamin-j.com. | ||
228 | + | ||
229 | +Copyright (c) 2008 Josh French, released under the MIT license |
@@ -0,0 +1,9 @@ | @@ -0,0 +1,9 @@ | ||
1 | +require 'bundler' | ||
2 | +Bundler::GemHelper.install_tasks | ||
3 | + | ||
4 | +require 'rspec/core/rake_task' | ||
5 | +RSpec::Core::RakeTask.new do |spec| | ||
6 | + spec.rspec_opts = ["--color", "--format progress"] | ||
7 | +end | ||
8 | + | ||
9 | +task :default => :spec | ||
0 | \ No newline at end of file | 10 | \ No newline at end of file |
@@ -0,0 +1,88 @@ | @@ -0,0 +1,88 @@ | ||
1 | +require 'net/http' | ||
2 | +require 'uri' | ||
3 | +require 'cgi' | ||
4 | +require 'yaml' | ||
5 | + | ||
6 | +require 'rakismet/model' | ||
7 | +require 'rakismet/middleware' | ||
8 | +require 'rakismet/version' | ||
9 | +require 'rakismet/railtie.rb' if defined?(Rails) | ||
10 | + | ||
11 | +module Rakismet | ||
12 | + Request = Struct.new(:user_ip, :user_agent, :referrer) | ||
13 | + Undefined = Class.new(NameError) | ||
14 | + | ||
15 | + class << self | ||
16 | + attr_accessor :key, :url, :host, :proxy_host, :proxy_port, :test | ||
17 | + | ||
18 | + def request | ||
19 | + @request ||= Request.new | ||
20 | + end | ||
21 | + | ||
22 | + def set_request_vars(env) | ||
23 | + request.user_ip, request.user_agent, request.referrer = | ||
24 | + env['REMOTE_ADDR'], env['HTTP_USER_AGENT'], env['HTTP_REFERER'] | ||
25 | + end | ||
26 | + | ||
27 | + def clear_request | ||
28 | + @request = Request.new | ||
29 | + end | ||
30 | + | ||
31 | + def headers | ||
32 | + @headers ||= begin | ||
33 | + user_agent = "Rakismet/#{Rakismet::VERSION}" | ||
34 | + user_agent = "Rails/#{Rails.version} | " + user_agent if defined?(Rails) | ||
35 | + { 'User-Agent' => user_agent, 'Content-Type' => 'application/x-www-form-urlencoded' } | ||
36 | + end | ||
37 | + end | ||
38 | + | ||
39 | + def validate_key | ||
40 | + validate_config | ||
41 | + akismet = URI.parse(verify_url) | ||
42 | + response = Net::HTTP::Proxy(proxy_host, proxy_port).start(akismet.host) do |http| | ||
43 | + data = "key=#{Rakismet.key}&blog=#{Rakismet.url}" | ||
44 | + http.post(akismet.path, data, Rakismet.headers) | ||
45 | + end | ||
46 | + @valid_key = (response.body == 'valid') | ||
47 | + end | ||
48 | + | ||
49 | + def valid_key? | ||
50 | + @valid_key == true | ||
51 | + end | ||
52 | + | ||
53 | + def akismet_call(function, args={}) | ||
54 | + validate_config | ||
55 | + args.merge!(:blog => Rakismet.url, :is_test => Rakismet.test_mode) | ||
56 | + akismet = URI.parse(call_url(function)) | ||
57 | + response = Net::HTTP::Proxy(proxy_host, proxy_port).start(akismet.host) do |http| | ||
58 | + params = args.map do |k,v| | ||
59 | + param = v.class < String ? v.to_str : v.to_s # for ActiveSupport::SafeBuffer and Nil, respectively | ||
60 | + "#{k}=#{CGI.escape(param)}" | ||
61 | + end | ||
62 | + http.post(akismet.path, params.join('&'), Rakismet.headers) | ||
63 | + end | ||
64 | + response.body | ||
65 | + end | ||
66 | + | ||
67 | + protected | ||
68 | + | ||
69 | + def verify_url | ||
70 | + "http://#{Rakismet.host}/1.1/verify-key" | ||
71 | + end | ||
72 | + | ||
73 | + def call_url(function) | ||
74 | + "http://#{Rakismet.key}.#{Rakismet.host}/1.1/#{function}" | ||
75 | + end | ||
76 | + | ||
77 | + def validate_config | ||
78 | + raise Undefined, "Rakismet.key is not defined" if Rakismet.key.nil? || Rakismet.key.empty? | ||
79 | + raise Undefined, "Rakismet.url is not defined" if Rakismet.url.nil? || Rakismet.url.empty? | ||
80 | + raise Undefined, "Rakismet.host is not defined" if Rakismet.host.nil? || Rakismet.host.empty? | ||
81 | + end | ||
82 | + | ||
83 | + def test_mode | ||
84 | + test ? 1 : 0 | ||
85 | + end | ||
86 | + end | ||
87 | + | ||
88 | +end |
@@ -0,0 +1,86 @@ | @@ -0,0 +1,86 @@ | ||
1 | +module Rakismet | ||
2 | + module Model | ||
3 | + | ||
4 | + def self.included(base) | ||
5 | + base.class_eval do | ||
6 | + attr_accessor :akismet_response | ||
7 | + class << self; attr_accessor :akismet_attrs; end | ||
8 | + extend ClassMethods | ||
9 | + include InstanceMethods | ||
10 | + self.rakismet_attrs | ||
11 | + end | ||
12 | + end | ||
13 | + | ||
14 | + module ClassMethods | ||
15 | + def rakismet_attrs(args={}) | ||
16 | + self.akismet_attrs ||= {} | ||
17 | + [:comment_type, :author, :author_url, :author_email, :content, :user_role].each do |field| | ||
18 | + # clunky, but throwing around +type+ will break your heart | ||
19 | + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern | ||
20 | + self.akismet_attrs[fieldname] = args.delete(field) || field | ||
21 | + end | ||
22 | + [:user_ip, :user_agent, :referrer].each do |field| | ||
23 | + self.akismet_attrs[field] = args.delete(field) || field | ||
24 | + end | ||
25 | + args.each_pair do |f,v| | ||
26 | + self.akismet_attrs[f] = v | ||
27 | + end | ||
28 | + end | ||
29 | + | ||
30 | + def inherited(subclass) | ||
31 | + super | ||
32 | + subclass.rakismet_attrs akismet_attrs.dup | ||
33 | + end | ||
34 | + end | ||
35 | + | ||
36 | + module InstanceMethods | ||
37 | + def spam? | ||
38 | + if instance_variable_defined? :@_spam | ||
39 | + @_spam | ||
40 | + else | ||
41 | + data = akismet_data | ||
42 | + self.akismet_response = Rakismet.akismet_call('comment-check', data) | ||
43 | + @_spam = self.akismet_response == 'true' | ||
44 | + end | ||
45 | + end | ||
46 | + | ||
47 | + def spam! | ||
48 | + Rakismet.akismet_call('submit-spam', akismet_data) | ||
49 | + @_spam = true | ||
50 | + end | ||
51 | + | ||
52 | + def ham! | ||
53 | + Rakismet.akismet_call('submit-ham', akismet_data) | ||
54 | + @_spam = false | ||
55 | + end | ||
56 | + | ||
57 | + private | ||
58 | + | ||
59 | + def akismet_data | ||
60 | + akismet = self.class.akismet_attrs.keys.inject({}) do |data,attr| | ||
61 | + mapped_field = self.class.akismet_attrs[attr] | ||
62 | + data.merge attr => if mapped_field.is_a?(Proc) | ||
63 | + instance_eval(&mapped_field) | ||
64 | + elsif !mapped_field.nil? && respond_to?(mapped_field) | ||
65 | + send(mapped_field) | ||
66 | + elsif not [:comment_type, :author, :author_email, | ||
67 | + :author_url, :content, :user_role, | ||
68 | + :user_ip, :referrer, | ||
69 | + :user_agent].include?(mapped_field) | ||
70 | + # we've excluded any fields that appear to | ||
71 | + # have their default unmapped values | ||
72 | + mapped_field | ||
73 | + elsif respond_to?(attr) | ||
74 | + send(attr) | ||
75 | + elsif Rakismet.request.respond_to?(attr) | ||
76 | + Rakismet.request.send(attr) | ||
77 | + end | ||
78 | + end | ||
79 | + akismet.delete_if { |k,v| v.nil? || v.empty? } | ||
80 | + akismet[:comment_type] ||= 'comment' | ||
81 | + akismet | ||
82 | + end | ||
83 | + end | ||
84 | + | ||
85 | + end | ||
86 | +end |
@@ -0,0 +1,22 @@ | @@ -0,0 +1,22 @@ | ||
1 | +require 'rails' | ||
2 | +require 'rakismet' | ||
3 | + | ||
4 | +module Rakismet | ||
5 | + class Railtie < Rails::Railtie | ||
6 | + | ||
7 | + config.rakismet = ActiveSupport::OrderedOptions.new | ||
8 | + config.rakismet.host = 'rest.akismet.com' | ||
9 | + config.rakismet.use_middleware = true | ||
10 | + | ||
11 | + initializer 'rakismet.setup', :after => :load_config_initializers do |app| | ||
12 | + Rakismet.key = app.config.rakismet[:key] | ||
13 | + Rakismet.url = app.config.rakismet[:url] | ||
14 | + Rakismet.host = app.config.rakismet[:host] | ||
15 | + Rakismet.proxy_host = app.config.rakismet[:proxy_host] | ||
16 | + Rakismet.proxy_port = app.config.rakismet[:proxy_port] | ||
17 | + Rakismet.test = app.config.rakismet.fetch(:test) { Rails.env.test? || Rails.env.development? } | ||
18 | + app.middleware.use Rakismet::Middleware if app.config.rakismet.use_middleware | ||
19 | + end | ||
20 | + | ||
21 | + end | ||
22 | +end |
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +# -*- encoding: utf-8 -*- | ||
2 | +require File.expand_path('../lib/rakismet/version', __FILE__) | ||
3 | + | ||
4 | +Gem::Specification.new do |s| | ||
5 | + s.name = "rakismet" | ||
6 | + s.version = Rakismet::VERSION | ||
7 | + s.platform = Gem::Platform::RUBY | ||
8 | + s.authors = ["Josh French"] | ||
9 | + s.email = "josh@vitamin-j.com" | ||
10 | + s.homepage = "http://github.com/joshfrench/rakismet" | ||
11 | + s.summary = "Akismet and TypePad AntiSpam integration for Rails." | ||
12 | + s.description = "Rakismet is the easiest way to integrate Akismet or TypePad's AntiSpam into your Rails app." | ||
13 | + s.date = "2012-04-22" | ||
14 | + | ||
15 | + s.rubyforge_project = "rakismet" | ||
16 | + s.add_development_dependency "rake" | ||
17 | + s.add_development_dependency "rspec", "~> 2.11" | ||
18 | + | ||
19 | + s.files = `git ls-files`.split("\n") | ||
20 | + s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") | ||
21 | + s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } | ||
22 | + s.require_paths = ["lib"] | ||
23 | + s.extra_rdoc_files = ["README.md"] | ||
24 | +end | ||
25 | + |
@@ -0,0 +1 @@ | @@ -0,0 +1 @@ | ||
1 | +--color |
vendor/plugins/rakismet/spec/models/block_params_spec.rb
0 → 100644
@@ -0,0 +1,25 @@ | @@ -0,0 +1,25 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +PROC = proc { author.reverse } | ||
4 | + | ||
5 | +class BlockAkismetModel | ||
6 | + include Rakismet::Model | ||
7 | + rakismet_attrs :author => PROC | ||
8 | +end | ||
9 | + | ||
10 | +describe BlockAkismetModel do | ||
11 | + | ||
12 | + before do | ||
13 | + @block = BlockAkismetModel.new | ||
14 | + comment_attrs.each_pair { |k,v| @block.stub!(k).and_return(v) } | ||
15 | + end | ||
16 | + | ||
17 | + it "should accept a block" do | ||
18 | + BlockAkismetModel.akismet_attrs[:comment_author].should eql(PROC) | ||
19 | + end | ||
20 | + | ||
21 | + it "should eval block with self = instance" do | ||
22 | + data = @block.send(:akismet_data) | ||
23 | + data[:comment_author].should eql(comment_attrs[:author].reverse) | ||
24 | + end | ||
25 | +end |
vendor/plugins/rakismet/spec/models/custom_params_spec.rb
0 → 100644
@@ -0,0 +1,20 @@ | @@ -0,0 +1,20 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +MAPPED_PARAMS = { :comment_type => :type2, :author => :author2, :content => :content2, | ||
4 | + :author_email => :author_email2, :author_url => :author_url2, | ||
5 | + :user_role => :user_role2 } | ||
6 | + | ||
7 | +class CustomAkismetModel | ||
8 | + include Rakismet::Model | ||
9 | + rakismet_attrs MAPPED_PARAMS.dup | ||
10 | +end | ||
11 | + | ||
12 | + | ||
13 | +describe CustomAkismetModel do | ||
14 | + it "should override default mappings" do | ||
15 | + [:comment_type, :author, :author_url, :author_email, :content, :user_role].each do |field| | ||
16 | + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern | ||
17 | + CustomAkismetModel.akismet_attrs[fieldname].should eql(MAPPED_PARAMS[field]) | ||
18 | + end | ||
19 | + end | ||
20 | +end |
vendor/plugins/rakismet/spec/models/extended_params_spec.rb
0 → 100644
@@ -0,0 +1,16 @@ | @@ -0,0 +1,16 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +EXTRA = { :extra => :extra, :another => lambda { } } | ||
4 | + | ||
5 | +class ExtendedAkismetModel | ||
6 | + include Rakismet::Model | ||
7 | + rakismet_attrs EXTRA.dup | ||
8 | +end | ||
9 | + | ||
10 | +describe ExtendedAkismetModel do | ||
11 | + it "should include additional attributes" do | ||
12 | + [:extra, :another].each do |field| | ||
13 | + ExtendedAkismetModel.akismet_attrs[field].should eql(EXTRA[field]) | ||
14 | + end | ||
15 | + end | ||
16 | +end |
vendor/plugins/rakismet/spec/models/rakismet_model_spec.rb
0 → 100644
@@ -0,0 +1,98 @@ | @@ -0,0 +1,98 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe AkismetModel do | ||
4 | + | ||
5 | + before do | ||
6 | + @model = AkismetModel.new | ||
7 | + comment_attrs.each_pair { |k,v| @model.stub!(k).and_return(v) } | ||
8 | + end | ||
9 | + | ||
10 | + it "should have default mappings" do | ||
11 | + [:comment_type, :author, :author_email, :author_url, :content, :user_role].each do |field| | ||
12 | + fieldname = field.to_s =~ %r(^comment_) ? field : "comment_#{field}".intern | ||
13 | + AkismetModel.akismet_attrs[fieldname].should eql(field) | ||
14 | + end | ||
15 | + end | ||
16 | + | ||
17 | + it "should have request mappings" do | ||
18 | + [:user_ip, :user_agent, :referrer].each do |field| | ||
19 | + AkismetModel.akismet_attrs[field].should eql(field) | ||
20 | + end | ||
21 | + end | ||
22 | + | ||
23 | + it "should populate comment type" do | ||
24 | + @model.send(:akismet_data)[:comment_type].should == comment_attrs[:comment_type] | ||
25 | + end | ||
26 | + | ||
27 | + describe ".spam?" do | ||
28 | + | ||
29 | + it "should use request variables from Rakismet.request if absent in model" do | ||
30 | + [:user_ip, :user_agent, :referrer].each do |field| | ||
31 | + @model.should_not respond_to(:field) | ||
32 | + end | ||
33 | + Rakismet.stub!(:request).and_return(request) | ||
34 | + Rakismet.should_receive(:akismet_call). | ||
35 | + with('comment-check', akismet_attrs.merge(:user_ip => '127.0.0.1', | ||
36 | + :user_agent => 'RSpec', | ||
37 | + :referrer => 'http://test.host/referrer')) | ||
38 | + @model.spam? | ||
39 | + end | ||
40 | + | ||
41 | + it "should cache result of #spam?" do | ||
42 | + Rakismet.should_receive(:akismet_call).once | ||
43 | + @model.spam? | ||
44 | + @model.spam? | ||
45 | + end | ||
46 | + | ||
47 | + it "should be true if comment is spam" do | ||
48 | + Rakismet.stub!(:akismet_call).and_return('true') | ||
49 | + @model.should be_spam | ||
50 | + end | ||
51 | + | ||
52 | + it "should be false if comment is not spam" do | ||
53 | + Rakismet.stub!(:akismet_call).and_return('false') | ||
54 | + @model.should_not be_spam | ||
55 | + end | ||
56 | + | ||
57 | + it "should set akismet_response" do | ||
58 | + Rakismet.stub!(:akismet_call).and_return('response') | ||
59 | + @model.spam? | ||
60 | + @model.akismet_response.should eql('response') | ||
61 | + end | ||
62 | + | ||
63 | + it "should not throw an error if request vars are missing" do | ||
64 | + Rakismet.stub!(:request).and_return(empty_request) | ||
65 | + lambda { @model.spam? }.should_not raise_error(NoMethodError) | ||
66 | + end | ||
67 | + end | ||
68 | + | ||
69 | + | ||
70 | + describe ".spam!" do | ||
71 | + it "should call Base.akismet_call with submit-spam" do | ||
72 | + Rakismet.should_receive(:akismet_call).with('submit-spam', akismet_attrs) | ||
73 | + @model.spam! | ||
74 | + end | ||
75 | + | ||
76 | + it "should mutate #spam?" do | ||
77 | + Rakismet.stub!(:akismet_call) | ||
78 | + @model.instance_variable_set(:@_spam, false) | ||
79 | + @model.spam! | ||
80 | + @model.should be_spam | ||
81 | + end | ||
82 | + end | ||
83 | + | ||
84 | + describe ".ham!" do | ||
85 | + it "should call Base.akismet_call with submit-ham" do | ||
86 | + Rakismet.should_receive(:akismet_call).with('submit-ham', akismet_attrs) | ||
87 | + @model.ham! | ||
88 | + end | ||
89 | + | ||
90 | + it "should mutate #spam?" do | ||
91 | + Rakismet.stub!(:akismet_call) | ||
92 | + @model.instance_variable_set(:@_spam, true) | ||
93 | + @model.ham! | ||
94 | + @model.should_not be_spam | ||
95 | + end | ||
96 | + end | ||
97 | + | ||
98 | +end |
vendor/plugins/rakismet/spec/models/request_params_spec.rb
0 → 100644
@@ -0,0 +1,23 @@ | @@ -0,0 +1,23 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +class RequestParams | ||
4 | + include Rakismet::Model | ||
5 | + attr_accessor :user_ip, :user_agent, :referrer | ||
6 | +end | ||
7 | + | ||
8 | +describe RequestParams do | ||
9 | + before do | ||
10 | + @model = RequestParams.new | ||
11 | + attrs = comment_attrs(:user_ip => '192.168.0.1', :user_agent => 'Rakismet', :referrer => 'http://localhost/referrer') | ||
12 | + attrs.each_pair { |k,v| @model.stub!(k).and_return(v) } | ||
13 | + end | ||
14 | + | ||
15 | + it "should use local values even if Rakismet.request is populated" do | ||
16 | + Rakismet.stub(:request).and_return(request) | ||
17 | + Rakismet.should_receive(:akismet_call). | ||
18 | + with('comment-check', akismet_attrs.merge(:user_ip => '192.168.0.1', | ||
19 | + :user_agent => 'Rakismet', | ||
20 | + :referrer => 'http://localhost/referrer')) | ||
21 | + @model.spam? | ||
22 | + end | ||
23 | +end |
@@ -0,0 +1,14 @@ | @@ -0,0 +1,14 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +class Subclass < AkismetModel | ||
4 | +end | ||
5 | + | ||
6 | +describe Subclass do | ||
7 | + it "should inherit parent's rakismet attrs" do | ||
8 | + Subclass.akismet_attrs.should eql AkismetModel.akismet_attrs # key/value equality | ||
9 | + end | ||
10 | + | ||
11 | + it "should get a new copy of parent's rakismet attrs" do | ||
12 | + Subclass.akismet_attrs.should_not equal AkismetModel.akismet_attrs # object equality | ||
13 | + end | ||
14 | +end |
vendor/plugins/rakismet/spec/rakismet_middleware_spec.rb
0 → 100644
@@ -0,0 +1,27 @@ | @@ -0,0 +1,27 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Rakismet::Middleware do | ||
4 | + | ||
5 | + let(:env) { { 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_USER_AGENT' => 'RSpec', 'HTTP_REFERER' => 'http://test.host/referrer' } } | ||
6 | + let(:app) { double(:app, :call => nil) } | ||
7 | + let(:request) { double(:request).as_null_object } | ||
8 | + | ||
9 | + before do | ||
10 | + @middleware = Rakismet::Middleware.new(app) | ||
11 | + end | ||
12 | + | ||
13 | + it "should set set Rakismet.request variables" do | ||
14 | + Rakismet.stub(:request).and_return(request) | ||
15 | + request.should_receive(:user_ip=).with('127.0.0.1') | ||
16 | + request.should_receive(:user_agent=).with('RSpec') | ||
17 | + request.should_receive(:referrer=).with('http://test.host/referrer') | ||
18 | + @middleware.call(env) | ||
19 | + end | ||
20 | + | ||
21 | + it "should clear Rakismet.request after request is complete" do | ||
22 | + @middleware.call(env) | ||
23 | + Rakismet.request.user_ip.should be_nil | ||
24 | + Rakismet.request.user_agent.should be_nil | ||
25 | + Rakismet.request.referrer.should be_nil | ||
26 | + end | ||
27 | +end |
@@ -0,0 +1,123 @@ | @@ -0,0 +1,123 @@ | ||
1 | +require 'spec_helper' | ||
2 | + | ||
3 | +describe Rakismet do | ||
4 | + | ||
5 | + def mock_response(body) | ||
6 | + double(:response, :body => body) | ||
7 | + end | ||
8 | + let(:http) { double(:http, :post => mock_response('akismet response')) } | ||
9 | + | ||
10 | + before do | ||
11 | + Rakismet.key = 'dummy-key' | ||
12 | + Rakismet.url = 'test.localhost' | ||
13 | + Rakismet.host = 'endpoint.localhost' | ||
14 | + end | ||
15 | + | ||
16 | + describe "proxy host" do | ||
17 | + it "should have proxy host and port as nil by default" do | ||
18 | + Rakismet.proxy_host.should be_nil | ||
19 | + Rakismet.proxy_port.should be_nil | ||
20 | + end | ||
21 | + end | ||
22 | + | ||
23 | + describe ".validate_config" do | ||
24 | + it "should raise an error if key is not found" do | ||
25 | + Rakismet.key = '' | ||
26 | + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined) | ||
27 | + end | ||
28 | + | ||
29 | + it "should raise an error if url is not found" do | ||
30 | + Rakismet.url = '' | ||
31 | + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined) | ||
32 | + end | ||
33 | + | ||
34 | + it "should raise an error if host is not found" do | ||
35 | + Rakismet.host = '' | ||
36 | + lambda { Rakismet.send(:validate_config) }.should raise_error(Rakismet::Undefined) | ||
37 | + end | ||
38 | + end | ||
39 | + | ||
40 | + describe ".validate_key" do | ||
41 | + before (:each) do | ||
42 | + @proxy = mock(Net::HTTP) | ||
43 | + Net::HTTP.stub!(:Proxy).and_return(@proxy) | ||
44 | + end | ||
45 | + | ||
46 | + it "should use proxy host and port" do | ||
47 | + Rakismet.proxy_host = 'proxy_host' | ||
48 | + Rakismet.proxy_port = 'proxy_port' | ||
49 | + @proxy.stub!(:start).and_return(mock_response('valid')) | ||
50 | + Net::HTTP.should_receive(:Proxy).with('proxy_host', 'proxy_port').and_return(@proxy) | ||
51 | + Rakismet.validate_key | ||
52 | + end | ||
53 | + | ||
54 | + it "should set @@valid_key = true if key is valid" do | ||
55 | + @proxy.stub!(:start).and_return(mock_response('valid')) | ||
56 | + Rakismet.validate_key | ||
57 | + Rakismet.valid_key?.should be_true | ||
58 | + end | ||
59 | + | ||
60 | + it "should set @@valid_key = false if key is invalid" do | ||
61 | + @proxy.stub!(:start).and_return(mock_response('invalid')) | ||
62 | + Rakismet.validate_key | ||
63 | + Rakismet.valid_key?.should be_false | ||
64 | + end | ||
65 | + | ||
66 | + it "should build url with host" do | ||
67 | + host = "api.antispam.typepad.com" | ||
68 | + Rakismet.host = host | ||
69 | + @proxy.should_receive(:start).with(host).and_yield(http) | ||
70 | + Rakismet.validate_key | ||
71 | + end | ||
72 | + end | ||
73 | + | ||
74 | + describe ".akismet_call" do | ||
75 | + before do | ||
76 | + @proxy = mock(Net::HTTP) | ||
77 | + Net::HTTP.stub!(:Proxy).and_return(@proxy) | ||
78 | + @proxy.stub(:start).and_yield(http) | ||
79 | + end | ||
80 | + | ||
81 | + it "should use proxy host and port" do | ||
82 | + Rakismet.proxy_host = 'proxy_host' | ||
83 | + Rakismet.proxy_port = 'proxy_port' | ||
84 | + @proxy.stub!(:start).and_return(mock_response('valid')) | ||
85 | + Net::HTTP.should_receive(:Proxy).with('proxy_host', 'proxy_port').and_return(@proxy) | ||
86 | + Rakismet.send(:akismet_call, 'bogus-function') | ||
87 | + end | ||
88 | + | ||
89 | + it "should build url with API key for the correct host" do | ||
90 | + host = 'api.antispam.typepad.com' | ||
91 | + Rakismet.host = host | ||
92 | + @proxy.should_receive(:start).with("#{Rakismet.key}.#{host}") | ||
93 | + Rakismet.send(:akismet_call, 'bogus-function') | ||
94 | + end | ||
95 | + | ||
96 | + it "should post data to named function" do | ||
97 | + http.should_receive(:post).with('/1.1/bogus-function', %r(foo=#{CGI.escape 'escape//this'}), Rakismet.headers) | ||
98 | + Rakismet.send(:akismet_call, 'bogus-function', { :foo => 'escape//this' }) | ||
99 | + end | ||
100 | + | ||
101 | + it "should default to not being in test mode" do | ||
102 | + http.should_receive(:post).with(anything, %r(is_test=0), anything) | ||
103 | + Rakismet.send(:akismet_call, 'bogus-function') | ||
104 | + end | ||
105 | + | ||
106 | + it "should be in test mode when configured" do | ||
107 | + Rakismet.test = true | ||
108 | + http.should_receive(:post).with(anything, %r(is_test=1), anything) | ||
109 | + Rakismet.send(:akismet_call, 'bogus-function') | ||
110 | + end | ||
111 | + | ||
112 | + it "should return response.body" do | ||
113 | + Rakismet.send(:akismet_call, 'bogus-function').should eql('akismet response') | ||
114 | + end | ||
115 | + | ||
116 | + it "should build query string when params are nil" do | ||
117 | + lambda { | ||
118 | + Rakismet.send(:akismet_call, 'bogus-function', { :nil_param => nil }) | ||
119 | + }.should_not raise_error(NoMethodError) | ||
120 | + end | ||
121 | + end | ||
122 | + | ||
123 | +end |
@@ -0,0 +1,34 @@ | @@ -0,0 +1,34 @@ | ||
1 | +require File.expand_path "lib/rakismet" | ||
2 | +require 'ostruct' | ||
3 | + | ||
4 | +RSpec.configure do |config| | ||
5 | + config.mock_with :rspec | ||
6 | +end | ||
7 | + | ||
8 | +class AkismetModel | ||
9 | + include Rakismet::Model | ||
10 | +end | ||
11 | + | ||
12 | +def comment_attrs(attrs={}) | ||
13 | + { :comment_type => 'test', :author => 'Rails test', | ||
14 | + :author_email => 'test@test.host', :author_url => 'test.host', | ||
15 | + :content => 'comment content', :blog => Rakismet.url }.merge(attrs) | ||
16 | +end | ||
17 | + | ||
18 | +def akismet_attrs(attrs={}) | ||
19 | + { :comment_type => 'test', :comment_author_email => 'test@test.host', | ||
20 | + :comment_author => 'Rails test', :comment_author_url => 'test.host', | ||
21 | + :comment_content => 'comment content' }.merge(attrs) | ||
22 | +end | ||
23 | + | ||
24 | +def request | ||
25 | + OpenStruct.new(:user_ip => '127.0.0.1', | ||
26 | + :user_agent => 'RSpec', | ||
27 | + :referrer => 'http://test.host/referrer') | ||
28 | +end | ||
29 | + | ||
30 | +def empty_request | ||
31 | + OpenStruct.new(:user_ip => nil, | ||
32 | + :user_agent => nil, | ||
33 | + :referrer => nil) | ||
34 | +end | ||
0 | \ No newline at end of file | 35 | \ No newline at end of file |