bradleypriest-mollom 0.2.3

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/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Jan De Poorter - Openminds BVBA
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,37 @@
1
+ == Mollom
2
+
3
+ This is a Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assessment.
4
+
5
+ == Installation
6
+
7
+ You can either install the library as a gem
8
+ gem install mollom
9
+
10
+ Or you can get the latest development version from GitHub:
11
+ git clone git://github.com/DefV/ruby-mollom.git
12
+
13
+ == Usage
14
+
15
+ After you have requested a public/private key-pair from Mollom (on http://www.mollom.com), you can start using this class.
16
+
17
+ require 'rubygems'
18
+ require 'mollom'
19
+
20
+ m = Mollom.new(:private_key => 'your-private-key', :public_key => 'your-public-key')
21
+
22
+ content = m.check_content(:post_title => 'Mollem is an open API',
23
+ :post_body => "Lorem Ipsum dolor...",
24
+ :author_name => 'Jan De Poorter',
25
+ :author_url => 'http://workswithruby.com')
26
+ if content.spam?
27
+ puts "You, sir, are a spammer.. Goodbye!"
28
+ elsif content.unsure?
29
+ # possible spam, possible ham, show CAPTCHA
30
+ puts "CAPTCHA: " + m.image_captcha(:session_id => content.session_id)["url"]
31
+
32
+ captcha_correct = m.check_captcha(:session_id => content.session_id, :solution => STDIN.gets.chomp)
33
+ else
34
+ puts "The post is perfect! No spam!"
35
+ end
36
+
37
+ Copyright (c) 2008 Jan De Poorter - Openminds BVBA, released under the MIT license
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ require 'rubygems'
2
+ require 'rake/rdoctask'
3
+ require 'rake/testtask'
4
+
5
+ begin
6
+ require 'jeweler'
7
+ Jeweler::Tasks.new do |gemspec|
8
+ gemspec.name = "bradleypriest-mollom"
9
+ gemspec.summary = "Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment."
10
+ gemspec.description = "Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment."
11
+ gemspec.email = "mollom@openminds.be"
12
+ gemspec.homepage = "http://mollom.rubyforge.com"
13
+ gemspec.authors = ["Jan De Poorter"]
14
+ end
15
+ Jeweler::GemcutterTasks.new
16
+ rescue LoadError
17
+ puts "Jeweler not available. Install it with: gem install jeweler"
18
+ end
19
+
20
+ Rake::TestTask.new do |t|
21
+ t.libs << "test"
22
+ t.test_files = FileList['test/*_test.rb']
23
+ t.verbose = true
24
+ end
25
+
26
+ Rake::RDocTask.new { |rdoc|
27
+ rdoc.rdoc_dir = 'docs'
28
+ rdoc.title = "Mollom -- Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment."
29
+ rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
30
+ rdoc.options << '--charset' << 'utf-8'
31
+ rdoc.rdoc_files.include('README.rdoc')
32
+ rdoc.rdoc_files.include('lib/**/*.rb')
33
+ }
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.2.3
@@ -0,0 +1,16 @@
1
+ class Mollom
2
+ module ApiCompatibility
3
+ def self.included base
4
+ base.class_eval do
5
+ alias checkContent check_content
6
+ alias getImageCaptcha image_captcha
7
+ alias getAudioCaptcha audio_captcha
8
+ alias checkCaptcha valid_captcha?
9
+ alias verifyKey key_ok?
10
+ alias getStatistics statistics
11
+ alias sendFeedback send_feedback
12
+ alias detectLanguage language_for
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,68 @@
1
+ class Mollom
2
+ class ContentResponse
3
+ attr_reader :session_id
4
+
5
+ # An assessment of the content's quality, between 0 and 1; 0 being very low, 1 being high quality if specified in check_content checks
6
+ attr_reader :quality
7
+
8
+ # An assessment of the content's profanity level, between 0 and 1; 0 being non-profane, 1 being very profane if specified in check_content checks
9
+ attr_reader :profanity
10
+
11
+ # An assessment of the content's sentiment, between 0 and 1; 0 being a very negative sentiment, 1 being a very positive sentiment if specified in check_content checks
12
+ attr_reader :sentiment
13
+
14
+ # a list of structs containing pairs of language and confidence values if specified in checkContent checks
15
+ attr_reader :language
16
+
17
+ Unknown = 0
18
+ Ham = 1
19
+ Spam = 2
20
+ Unsure = 3
21
+
22
+ # This class should only be initialized from within the +check_content+ command.
23
+ def initialize(hash)
24
+ @hash = hash
25
+ @spam_response = hash["spam"]
26
+ @session_id = hash["session_id"]
27
+ @quality = hash["quality"]
28
+ @profanity = hash["profanity"]
29
+ @sentiment = hash["sentiment"]
30
+ @language = hash["language"]
31
+ end
32
+
33
+ # Is the content Spam?
34
+ def spam?
35
+ @spam_response == Spam
36
+ end
37
+
38
+ # Is the content Ham?
39
+ def ham?
40
+ @spam_response == Ham
41
+ end
42
+
43
+ # is Mollom unsure about the content?
44
+ def unsure?
45
+ @spam_response == Unsure
46
+ end
47
+
48
+ # is the content unknown?
49
+ def unknown?
50
+ @spam_response == Unknown
51
+ end
52
+
53
+ # Returns 'unknown', 'ham', 'unsure' or 'spam', depending on what the content is.
54
+ def to_s
55
+ case @spam_response
56
+ when Unknown then 'unknown'
57
+ when Ham then 'ham'
58
+ when Unsure then 'unsure'
59
+ when Spam then 'spam'
60
+ end
61
+ end
62
+
63
+ # Returns the original hash for testing
64
+ def to_hash
65
+ @hash
66
+ end
67
+ end
68
+ end
data/lib/mollom.rb ADDED
@@ -0,0 +1,215 @@
1
+ require 'xmlrpc/client'
2
+ require 'openssl'
3
+ require 'base64'
4
+ require 'mollom/content_response'
5
+ require 'mollom/api_compatibility'
6
+
7
+ # Mollom API requires this to change, but this gives a warning!
8
+ # XMLRPC::Client::USER_AGENT = "Ruby Mollom/0.1"
9
+
10
+ class Mollom
11
+ API_VERSION = '1.0'
12
+ STATIC_SERVER_LIST = [{:proto => 'http', :host => 'xmlrpc3.mollom.com'},
13
+ {:proto => 'http', :host => 'xmlrpc2.mollom.com'},
14
+ {:proto => 'http', :host => 'xmlrpc1.mollom.com'}].freeze
15
+
16
+ module Errors
17
+ Standard = 1000
18
+ Refresh = 1100
19
+ TooBusy = 1200
20
+ end
21
+
22
+ attr_accessor :private_key, :public_key
23
+
24
+ # Creates a new Mollom object. Takes +private_key+ and +public_key+ as keys.
25
+ #
26
+ # Mollom.new(:private_key => 'qopzalnzanzajlazjna', :public_key => 'aksakzaddazidzaodjaz')
27
+ # # => #<Mollom:0x5b6454 @public_key="aksakzaddazidzaodjaz", @private_key="qopzalnzanzajlazjna">
28
+
29
+ def initialize options = {}
30
+ @private_key = options[:private_key]
31
+ @public_key = options[:public_key]
32
+ end
33
+
34
+ # Checks the content whether it is spam, ham (not spam), or undecided, and gives a quality assessment of the content.
35
+ # Possible content keys are:
36
+ # session_id # => If you allready have a session_id
37
+ # post_title # => The title
38
+ # post_body # => The main content of the post.
39
+ # author_name # => The name of the post author
40
+ # author_url # => The url the author enters
41
+ # author_mail # => The author's email address
42
+ # author_ip # => The author's IP address
43
+ # author_openid # => The author's OpenID
44
+ # author_id # => The author's ID
45
+ # checks # => A comma-separated list of checks. Available checks include 'spam', 'quality', 'profanity', 'sentiment' and 'language'. E.g. 'spam,quality,sentiment'
46
+ #
47
+ # Only the +post_body+ key is required, all other keys are optional.
48
+ # This function returns a ContentResponse object.
49
+ #
50
+ # response = mollom.check_content :post_title => 'Mollom rules!',
51
+ # :post_body => 'I think that mollom is so cool!',
52
+ # :author_name => 'Jan De Poorter',
53
+ # :author_url => 'http://www.openminds.be'
54
+ # response.spam? # => false
55
+ # response.ham? # => true
56
+ def check_content content = {}
57
+ ContentResponse.new(send_command('mollom.checkContent', content))
58
+ end
59
+
60
+ # Requests an Image captcha from Mollom. It takes the optional <tt>session_id</tt> and <tt>author_ip</tt> keys, if you allready have a session.
61
+ # It returns a hash with the URL where the captcha can be found, and the session_id, to keep track of the current session (Needed later in <tt>Mollom#check_captcha</tt>)
62
+ #
63
+ # captcha = mollom.image_captcha :author_ip => '172.16.0.1'
64
+ # captcha['url'] # => http://xmlrpc1.mollom.com:80/a9616e6b4cd6a81ecdd509fa624d895d.png
65
+ # captcha['session_id'] # => a9616e6b4cd6a81ecdd509fa624d895d
66
+ def image_captcha info = {}
67
+ send_command('mollom.getImageCaptcha', info)
68
+ end
69
+
70
+ # Requests an Audio captcha from Mollom. It takes the optional +session_id+ and +author_ip+ keys, if you allready have a session.
71
+ # It returns a hash with the URL where the captcha can be found, and the session_id, to keep track of the current session (Needed later in <tt>Mollom#check_captcha</tt>)
72
+ #
73
+ # captcha = mollom.audio_captcha :author_ip => '172.16.0.2', :session_id => 'a9616e6b4cd6a81ecdd509fa624d895d'
74
+ # captcha['url'] # => http://xmlrpc1.mollom.com:80/a9616e6b4cd6a81ecdd509fa624d895d.mp3
75
+ # captcha['session_id'] # => a9616e6b4cd6a81ecdd509fa624d895d
76
+ def audio_captcha info = {}
77
+ send_command('mollom.getAudioCaptcha', info)
78
+ end
79
+
80
+ # Checks with mollom if the given captcha (by the user) is correct. Takes +session_id+ and +solution+ keys. Both keys are required.
81
+ # Returns true if the captcha is valid, false if it is incorrect
82
+ #
83
+ # captcha = mollom.image_captcha :author_ip => '172.16.0.1'
84
+ # # show to user... input from user
85
+ # return = mollom.valid_captcha? :session_id => captcha['session_id'], :solution => 'abcDe9'
86
+ # return # => true
87
+ def valid_captcha? info = {}
88
+ send_command('mollom.checkCaptcha', info)
89
+ end
90
+
91
+ # Standard check to see if your public/private keypair are recognized. Takes no options
92
+ def key_ok?
93
+ send_command('mollom.verifyKey')
94
+ rescue XMLRPC::FaultException
95
+ false
96
+ end
97
+
98
+ # Gets some statistics from Mollom about your site.
99
+ #
100
+ # The type has to be passed. Possible types:
101
+ # total_days
102
+ # total_accepted
103
+ # total_rejected
104
+ # yesterday_accepted
105
+ # yesterday_rejected
106
+ # today_accepted
107
+ # today_rejected
108
+ #
109
+ # mollom.statistics :type => 'total_accepted' # => 123
110
+ def statistics options = {}
111
+ send_command('mollom.getStatistics', options)
112
+ end
113
+
114
+ # Send feedback to Mollom about a certain content. Required keys are +session_id+ and +feedback+.
115
+ #
116
+ # Feedback can be any of
117
+ # spam
118
+ # profanity
119
+ # low-quality
120
+ # unwanted
121
+ #
122
+ # mollom.send_feedback :session_id => 'a9616e6b4cd6a81ecdd509fa624d895d', :feedback => 'unwanted'
123
+ def send_feedback feedback = {}
124
+ send_command('mollom.sendFeedback', feedback)
125
+ end
126
+
127
+ # Gets language of input text.
128
+ #
129
+ # mollom.language_for 'This is my text'
130
+ def language_for text
131
+ send_command('mollom.detectLanguage', :text => text)
132
+ end
133
+
134
+ # Gets a list of servers from Mollom. You should cache this information in your application in a temporary file or in a database. You can set this with Mollom#server_list=
135
+ #
136
+ # Takes an optional parameter +refresh+, which resets the cached value.
137
+ #
138
+ # mollom.server_list
139
+ # # => [{:proto=>"http", :host=>"88.151.243.81"}, {:proto=>"http", :host=>"82.103.131.136"}]
140
+ def server_list refresh = false
141
+ return @server_list if @server_list && !refresh
142
+ STATIC_SERVER_LIST.each do |static_server|
143
+ @server_list = get_server_list_from(static_server)
144
+ return @server_list if @server_list
145
+ end
146
+ # Should have returned a server_list here..
147
+ raise(Error.new("Can't get mollom server-list"))
148
+ end
149
+
150
+ # Sets the server list used to contact Mollom. This should be used to set the list of cached servers.
151
+ #
152
+ # If you try to set a faulty server list, the function will silently fail, so we can get the server-list from Mollom.
153
+ def server_list=(list)
154
+ # Check if we get an actual serverlist-array
155
+ if list.is_a?(Array) && list.all? {|hash| hash.has_key?(:host) && hash.has_key?(:proto) }
156
+ @server_list = list
157
+ end
158
+ end
159
+
160
+ private
161
+ def get_server_list_from(server)
162
+ XMLRPC::Client.new(server[:host], "/#{API_VERSION}").call('mollom.getServerList', authentication_hash).collect do |server|
163
+ proto, ip = server.split('://')
164
+ {:proto => proto, :host => ip}
165
+ end
166
+ rescue StandardError, Exception
167
+ nil
168
+ end
169
+
170
+ def send_command(command, data = {})
171
+ server_list.each do |server|
172
+ begin
173
+ client = XMLRPC::Client.new(server[:host], "/#{API_VERSION}")
174
+ return client.call(command, data.merge(authentication_hash))
175
+ # TODO: Rescue more stuff (Connection Timeout and such)
176
+ rescue RuntimeError
177
+ next
178
+ rescue XMLRPC::FaultException => error
179
+ case error.faultCode
180
+ when Errors::Standard
181
+ raise Error.new(error.faultString)
182
+ when Errors::Refresh # Refresh server list please!
183
+ # we take this one out of our loop
184
+ raise
185
+ when Errors::TooBusy # Server is too busy, take the next one
186
+ next
187
+ else
188
+ next
189
+ end
190
+ end
191
+ end
192
+ raise Mollom::NoAvailableServers
193
+ rescue XMLRPC::FaultException
194
+ # We know it is Errors::Refresh
195
+ server_list(true)
196
+ retry
197
+ end
198
+
199
+ # Creates a HMAC-SHA1 Hash with the current timestamp, a nonce, and your private key.
200
+ def authentication_hash
201
+ now = Time.now.gmtime.strftime('%Y-%m-%dT%H:%M:%S.000+0000')
202
+ nonce = Kernel.rand(2**31) # Random signed int
203
+
204
+ hash = Base64.encode64(
205
+ OpenSSL::HMAC.digest(OpenSSL::Digest::SHA1.new, @private_key, "#{now}:#{nonce}:#{@private_key}")
206
+ ).chomp
207
+
208
+ return :public_key=> @public_key, :time => now, :hash => hash, :nonce => nonce
209
+ end
210
+
211
+ class Error < StandardError; end
212
+ class NoAvailableServers < Error; end
213
+
214
+ include ApiCompatibility
215
+ end
data/mollom.gemspec ADDED
@@ -0,0 +1,52 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{mollom}
8
+ s.version = "0.2.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Jan De Poorter"]
12
+ s.date = %q{2010-07-26}
13
+ s.description = %q{Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment.}
14
+ s.email = %q{mollom@openminds.be}
15
+ s.extra_rdoc_files = [
16
+ "README.rdoc"
17
+ ]
18
+ s.files = [
19
+ ".gitignore",
20
+ "MIT-LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "lib/mollom.rb",
25
+ "lib/mollom/api_compatibility.rb",
26
+ "lib/mollom/content_response.rb",
27
+ "mollom.gemspec",
28
+ "pkg/mollom-0.1.1.gem",
29
+ "test/content_response_test.rb",
30
+ "test/mollom_test.rb"
31
+ ]
32
+ s.homepage = %q{http://mollom.rubyforge.com}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment.}
37
+ s.test_files = [
38
+ "test/content_response_test.rb",
39
+ "test/mollom_test.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ else
48
+ end
49
+ else
50
+ end
51
+ end
52
+
Binary file
@@ -0,0 +1,33 @@
1
+ require "test/unit"
2
+
3
+ require "mollom"
4
+
5
+ class TestContentResponse < Test::Unit::TestCase
6
+ def test_spam
7
+ cr = Mollom::ContentResponse.new('spam' => 2, 'session_id' => '1', 'quality' => '10')
8
+ assert cr.spam?
9
+ assert !cr.ham?
10
+ assert_equal 'spam', cr.to_s
11
+ end
12
+
13
+ def test_ham
14
+ cr = Mollom::ContentResponse.new('spam' => 1, 'session_id' => '1', 'quality' => '10')
15
+ assert cr.ham?
16
+ assert !cr.spam?
17
+ assert_equal 'ham', cr.to_s
18
+ end
19
+
20
+ def test_unknown
21
+ cr = Mollom::ContentResponse.new('spam' => 0, 'session_id' => '1', 'quality' => '10')
22
+ assert cr.unknown?
23
+ assert !cr.unsure?
24
+ assert_equal 'unknown', cr.to_s
25
+ end
26
+
27
+ def test_unsure
28
+ cr = Mollom::ContentResponse.new('spam' => 3, 'session_id' => '1', 'quality' => '10')
29
+ assert cr.unsure?
30
+ assert !cr.unknown?
31
+ assert_equal 'unsure', cr.to_s
32
+ end
33
+ end
@@ -0,0 +1,271 @@
1
+ require 'test/unit'
2
+ require 'rubygems'
3
+ require 'mocha'
4
+
5
+ require 'mollom'
6
+
7
+ class Mollom
8
+ # Unprivate all methods
9
+ public :authentication_hash
10
+ public :server_list
11
+ public :send_command
12
+ end
13
+
14
+ class TestMollom < Test::Unit::TestCase
15
+ def setup
16
+ @mollom = Mollom.new(:private_key => 'xxxxxxxxx', :public_key => 'yyyyyyyyy')
17
+ end
18
+
19
+ def test_initialize
20
+ assert_equal 'xxxxxxxxx', @mollom.private_key
21
+ assert_equal 'yyyyyyyyy', @mollom.public_key
22
+ end
23
+
24
+ def test_authentication_hash
25
+ time = mock
26
+ time.expects(:strftime).with('%Y-%m-%dT%H:%M:%S.000+0000').returns('2008-04-01T13:54:26.000+0000')
27
+ Time.expects(:now).returns(stub(:gmtime => time))
28
+ Kernel.expects(:rand).with(2**31).returns(42)
29
+ hash = @mollom.authentication_hash
30
+ assert_equal("oWN15TqrbLVdTAgcuDmofskaNyM=", hash[:hash])
31
+ assert_equal('yyyyyyyyy', hash[:public_key])
32
+ assert_equal('2008-04-01T13:54:26.000+0000', hash[:time])
33
+ assert_equal(42, hash[:nonce])
34
+ end
35
+
36
+ def test_server_list
37
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc3.mollom.com', :proto => 'http').returns([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}])
38
+ assert_equal [{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}], @mollom.server_list
39
+ end
40
+
41
+ def test_server_list_with_first_server_bad
42
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc3.mollom.com', :proto => 'http').returns(nil)
43
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc2.mollom.com', :proto => 'http').returns([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}])
44
+ assert_equal [{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}], @mollom.server_list
45
+ end
46
+
47
+ def test_server_list_with_all_servers_bad
48
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc3.mollom.com', :proto => 'http').returns(nil)
49
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc2.mollom.com', :proto => 'http').returns(nil)
50
+ Mollom.any_instance.expects(:get_server_list_from).with(:host => 'xmlrpc1.mollom.com', :proto => 'http').returns(nil)
51
+ assert_raise(Mollom::Error) { @mollom.server_list }
52
+ end
53
+
54
+ def test_get_server_list_from_with_ok_return
55
+ xml_rpc = mock
56
+ xml_rpc.expects(:call).times(1).with('mollom.getServerList', is_a(Hash)).returns(['http://172.16.0.1', 'http://172.16.0.2', 'https://172.16.0.2'])
57
+ XMLRPC::Client.stubs(:new).with('xmlrpc.mollom.com', '/1.0').returns(xml_rpc)
58
+ assert_equal([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}], @mollom.send(:get_server_list_from, {:host => 'xmlrpc.mollom.com', :proto => 'http'}))
59
+ end
60
+
61
+ def test_get_server_list_from_with_raising_return
62
+ xml_rpc = mock
63
+ xml_rpc.expects(:call).times(1).with('mollom.getServerList', is_a(Hash)).raises(XMLRPC::FaultException.new(1000, "Broken mollom"))
64
+ XMLRPC::Client.stubs(:new).with('xmlrpc.mollom.com', '/1.0').returns(xml_rpc)
65
+ assert_equal(nil, @mollom.send(:get_server_list_from, {:host => 'xmlrpc.mollom.com', :proto => 'http'}))
66
+ end
67
+
68
+ def test_get_server_list_from_with_timemout
69
+ xml_rpc = mock
70
+ xml_rpc.expects(:call).times(1).with('mollom.getServerList', is_a(Hash)).raises(Timeout::Error)
71
+ XMLRPC::Client.stubs(:new).with('xmlrpc.mollom.com', '/1.0').returns(xml_rpc)
72
+ assert_equal(nil, @mollom.send(:get_server_list_from, {:host => 'xmlrpc.mollom.com', :proto => 'http'}))
73
+ end
74
+
75
+ def test_server_list_force_reload
76
+ Mollom.any_instance.expects(:get_server_list_from).times(2).with(:host => 'xmlrpc3.mollom.com', :proto => 'http').returns([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'https'}])
77
+
78
+ @mollom.server_list
79
+ @mollom.server_list
80
+ @mollom.server_list
81
+ @mollom.server_list(true)
82
+ end
83
+
84
+ def test_server_list_setter_with_good_list
85
+ @mollom.server_list = [{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}]
86
+ assert_equal [{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}], @mollom.instance_variable_get('@server_list')
87
+ end
88
+
89
+ def test_send_command_with_old_server_list
90
+ @mollom.server_list = [{:ip => '172.16.0.1', :proto => 'http'}, {:ip => '172.16.0.2', :proto => 'http'}]
91
+ assert_equal nil, @mollom.instance_variable_get('@server_list')
92
+ end
93
+
94
+ def test_send_command_with_bad_server_list
95
+ @mollom.server_list = "404 Not Found Bad User Input"
96
+ assert_equal nil, @mollom.instance_variable_get('@server_list')
97
+ end
98
+
99
+
100
+ def test_send_command_with_good_server
101
+ Mollom.any_instance.expects(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}])
102
+ xml_rpc = mock
103
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo'))
104
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
105
+
106
+ @mollom.send_command('mollom.testMessage', {:options => 'foo'})
107
+ end
108
+
109
+
110
+ def test_send_command_with_bad_http_response
111
+ Mollom.any_instance.expects(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}])
112
+ xml_rpc = mock
113
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo')).raises(RuntimeError.new('HTTP-Error: 302 Found'))
114
+ xml_rpc2 = mock
115
+ xml_rpc2.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo'))
116
+
117
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
118
+ XMLRPC::Client.expects(:new).with('172.16.0.2', '/1.0').returns(xml_rpc2)
119
+ @mollom.send_command('mollom.testMessage', {:options => 'foo'})
120
+ end
121
+
122
+ def test_send_command_with_bad_server
123
+ Mollom.any_instance.expects(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}, {:host => '172.16.0.2', :proto => 'http'}])
124
+ xml_rpc = mock
125
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo')).raises(XMLRPC::FaultException.new(1200, "Redirect"))
126
+ xml_rpc2 = mock
127
+ xml_rpc2.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo'))
128
+
129
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
130
+ XMLRPC::Client.expects(:new).with('172.16.0.2', '/1.0').returns(xml_rpc2)
131
+ @mollom.send_command('mollom.testMessage', {:options => 'foo'})
132
+ end
133
+
134
+ def test_send_command_with_reload_exception
135
+ Mollom.any_instance.stubs(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}], [{:host => '172.16.0.2', :proto => 'http'}])
136
+ xml_rpc = mock
137
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo')).raises(XMLRPC::FaultException.new(1100, "Refresh"))
138
+ xml_rpc2 = mock
139
+ xml_rpc2.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo'))
140
+
141
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
142
+ XMLRPC::Client.expects(:new).with('172.16.0.2', '/1.0').returns(xml_rpc2)
143
+ @mollom.send_command('mollom.testMessage', {:options => 'foo'})
144
+ end
145
+
146
+ def test_send_command_with_bad_command
147
+ Mollom.any_instance.expects(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}])
148
+ xml_rpc = mock
149
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo')).raises(XMLRPC::FaultException.new(1000, "Fault String"))
150
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
151
+
152
+ assert_raise(Mollom::Error) { @mollom.send_command('mollom.testMessage', {:options => 'foo'}) }
153
+ end
154
+
155
+ def test_send_command_with_bad_server_and_no_more_available
156
+ Mollom.any_instance.expects(:server_list).returns([{:host => '172.16.0.1', :proto => 'http'}])
157
+ xml_rpc = mock
158
+ xml_rpc.expects(:call).with('mollom.testMessage', has_entry(:options => 'foo')).raises(XMLRPC::FaultException.new(1200, "Redirect"))
159
+
160
+ XMLRPC::Client.expects(:new).with('172.16.0.1', '/1.0').returns(xml_rpc)
161
+
162
+ assert_raise(Mollom::NoAvailableServers) { @mollom.send_command('mollom.testMessage', {:options => 'foo'}) }
163
+ end
164
+
165
+ def test_check_content
166
+ options = {:author_ip => '172.16.0.1', :post_body => 'Lorem Ipsum'}
167
+
168
+ assert_command 'mollom.checkContent', :with => options, :returns => {"spam" => 1, "quality" => 0.40, "session_id" => 1} do
169
+ cr = @mollom.check_content(options)
170
+ assert cr.ham?
171
+ assert_equal 1, cr.session_id
172
+ assert_equal 0.40, cr.quality
173
+ end
174
+ end
175
+
176
+ def test_check_content_with_checks
177
+ options = {:author_ip => '172.16.0.1', :post_body => 'Lorem Ipsum', :checks => 'spam,quality,sentiment'}
178
+
179
+ assert_command 'mollom.checkContent', :with => options, :returns => {"spam" => 1, "quality" => 0.40, "session_id" => 1, "sentiment" => 0.25, 'profanity' => 0.10 } do
180
+ cr = @mollom.check_content(options)
181
+ assert cr.ham?
182
+ assert_equal 1, cr.session_id
183
+ assert_equal 0.40, cr.quality
184
+ assert_equal 0.25, cr.sentiment
185
+ assert_equal 0.10, cr.profanity
186
+ assert cr.respond_to? :to_hash
187
+ end
188
+ end
189
+
190
+ def test_image_captcha
191
+ options = {:author_ip => '172.16.0.1'}
192
+
193
+ assert_command 'mollom.getImageCaptcha', :with => options, :returns => {'url' => 'http://xmlrpc1.mollom.com:80/a9616e6b4cd6a81ecdd509fa624d895d.png', 'session_id' => 'a9616e6b4cd6a81ecdd509fa624d895d'} do
194
+ @mollom.image_captcha(:author_ip => '172.16.0.1')
195
+ end
196
+ end
197
+
198
+ def test_audio_captcha
199
+ options = {:author_ip => '172.16.0.1'}
200
+
201
+ assert_command 'mollom.getAudioCaptcha', :with => options, :returns => {'url' => 'http://xmlrpc1.mollom.com:80/a9616e6b4cd6a81ecdd509fa624d895d.mp3', 'session_id' => 'a9616e6b4cd6a81ecdd509fa624d895d'} do
202
+ @mollom.audio_captcha(options)
203
+ end
204
+ end
205
+
206
+ def test_valid_captcha
207
+ options = {:session_id => 'a9616e6b4cd6a81ecdd509fa624d895d', :solution => 'foo'}
208
+
209
+ assert_command 'mollom.checkCaptcha', :with => options, :returns => true do
210
+ assert @mollom.valid_captcha?(options)
211
+ end
212
+ end
213
+
214
+ def test_key_ok
215
+ assert_command 'mollom.verifyKey', :returns => true do
216
+ assert @mollom.key_ok?
217
+ end
218
+ end
219
+
220
+ def test_invalid_key
221
+ assert_command 'mollom.verifyKey', :raises => internal_server_error do
222
+ assert !@mollom.key_ok?
223
+ end
224
+ end
225
+
226
+ def test_statistics
227
+ assert_command 'mollom.getStatistics', :with => {:type => 'total_accepted'}, :returns => 12 do
228
+ @mollom.statistics(:type => 'total_accepted')
229
+ end
230
+ end
231
+
232
+ def test_send_feedback
233
+ assert_command 'mollom.sendFeedback', :with => {:session_id => 1, :feedback => 'profanity'} do
234
+ @mollom.send_feedback :session_id => 1, :feedback => 'profanity'
235
+ end
236
+ end
237
+
238
+ def test_detect_language
239
+ assert_command 'mollom.detectLanguage', :with => {:text => 'Dit is nederlands'}, :returns => [{"confidence"=>0.332, "language"=>"nl"}] do
240
+ assert_equal [{"confidence"=>0.332, "language"=>"nl"}], @mollom.language_for('Dit is nederlands')
241
+ end
242
+ end
243
+
244
+ def test_api_compatability
245
+ assert @mollom.respond_to? :checkContent
246
+ assert @mollom.respond_to? :getImageCaptcha
247
+ assert @mollom.respond_to? :getAudioCaptcha
248
+ assert @mollom.respond_to? :checkCaptcha
249
+ assert @mollom.respond_to? :verifyKey
250
+ assert @mollom.respond_to? :getStatistics
251
+ assert @mollom.respond_to? :sendFeedback
252
+ assert @mollom.respond_to? :detectLanguage
253
+ end
254
+
255
+ private
256
+ def assert_command command, options = {}
257
+ expectation = Mollom.any_instance.expects(:send_command)
258
+ expectation.with do |*arguments|
259
+ arguments.first == command &&
260
+ (!options[:with] || options[:with] == arguments.last)
261
+ end
262
+ expectation.returns(options[:returns]) if options[:returns]
263
+ expectation.raises(options[:raises]) if options[:raises]
264
+
265
+ yield
266
+ end
267
+
268
+ def internal_server_error
269
+ XMLRPC::FaultException.new(1000, "Internal server error due to malformed request, or the hamster powering the server died...")
270
+ end
271
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bradleypriest-mollom
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.2.3
6
+ platform: ruby
7
+ authors:
8
+ - Jan De Poorter
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-04-12 00:00:00 +12:00
14
+ default_executable:
15
+ dependencies: []
16
+
17
+ description: Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment.
18
+ email: mollom@openminds.be
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files:
24
+ - README.rdoc
25
+ files:
26
+ - MIT-LICENSE
27
+ - README.rdoc
28
+ - Rakefile
29
+ - VERSION
30
+ - lib/mollom.rb
31
+ - lib/mollom/api_compatibility.rb
32
+ - lib/mollom/content_response.rb
33
+ - mollom.gemspec
34
+ - pkg/mollom-0.1.1.gem
35
+ - test/content_response_test.rb
36
+ - test/mollom_test.rb
37
+ has_rdoc: true
38
+ homepage: http://mollom.rubyforge.com
39
+ licenses: []
40
+
41
+ post_install_message:
42
+ rdoc_options: []
43
+
44
+ require_paths:
45
+ - lib
46
+ required_ruby_version: !ruby/object:Gem::Requirement
47
+ none: false
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ requirements: []
59
+
60
+ rubyforge_project:
61
+ rubygems_version: 1.5.2
62
+ signing_key:
63
+ specification_version: 3
64
+ summary: Ruby class for easy interfacing with the mollom.com open API for spam detection and content quality assesment.
65
+ test_files:
66
+ - test/content_response_test.rb
67
+ - test/mollom_test.rb