On 9/6/06, Sean C. [email protected] wrote:
One user, “Nolan”, commented that CAPTCHA images are inaccessible (to
vision-impaired users) and should not be considered an option. As an
alternative, he suggested the use of spam-filtering services like Akismet.
While this seems a very promising option, that would be a whole lot of extra
code (and perhaps another library, even a gem!) and probably outside the
scope of my little behaviors.
If you write directly to the service by doing your own POST and parse
the results back, it should be doable in 200 lines or less. But you
would introduce depedencies on rexml and open-uri. I interfaced with
Flickr recently by doing the ff.
- My model objects are based off
class Base
def self.create(options)
object = self.new
options.each_pair { |k,v| object.method("#{k}=").call(v) }
object
end
end
To make them more ActiveRecord-like (am not using a database in this
particular app.
- Call the REST api by constructing a URL, passing that to open-uri,
with a block that both selects what you want, and parses the response.
build the URL
def self.flickr(method, token, options,
rest="http://www.flickr.com/services/rest/?")
raise FlickrError.new(“hash expected”) unless options.is_a?(Hash)
raise FlickrError.new(“url expected”) unless rest &&
rest.is_a?(String)
options.update(‘api_key’ => @@api_id)
options.update(‘method’ => api(method)) if method
options.update(‘auth_token’ => token) if token
signature = options.keys.sort.map { |k| “#{k}#{options[k]}”
}.join(’’)
parameters = options.keys.sort.map { |k|
“#{k}=#{url_escape(options[k])}” }.join(’&’)
url = rest + parameters +
“&api_sig=#{Digest::MD5::hexdigest(@@shared_secret + signature)}”
logger.debug “url: #{url}”
url
end
Now fetch and parse it
def self.fetch(url, xpath, &block)
open(url) do |reponse|
xml = reponse.read
logger.debug “xml: #{xml}”
doc = REXML::Document.new xml
doc.elements.each(xpath) { |element| yield element if block_given?
}
end
end
And here’s how I use it
def self.find(token, method, options)
photos = []
fetch(flickr(method, token, options), ‘rsp/photos/photo’) do
|element|
photos << Photo.create(
:id => element.attributes[‘id’],
:secret => element.attributes[‘secret’],
:title => element.attributes[‘title’],
:server => element.attributes[‘server’],
:ispublic => element.attributes[‘ispublic’] == ‘1’,
:isfriend => element.attributes[‘isfriend’] == ‘1’,
:isfamily => element.attributes[‘isfamily’] == ‘1’,
:owner => element.attributes[‘owner’],
:longitude => element.attributes[‘longitude’].to_f,
:latitude => element.attributes[‘latitude’].to_f,
:geotagged => (element.attributes[‘longitude’].to_f != 0.0 ||
element.attributes[‘latitude’].to_f != 0.0)
) if element.attributes[‘id’]
end
photos
end
Simple and can be kept in only one extra file. If that file defines an
interface, then others can be written to handle different spam
services.
Instead I’d like to get your thoughts on another method, although there is
ample room for many methods down the road. The method I’m considering is a
challenge-response where the challenge is in human-readable text (a method
suggested on some of the CAPTCHA pages I read). I haven’t worked out the
particulars, but it would go something like “Subtract twenty-five from the
sum of eighty and thirty-seven and enter the result as a number.” The real
ones would probably be easier than that, of course. This could be
automatically generated of course. Another option is to say “Enter the
subject of the sentence ‘Jane walked to school.’” or something of that sort.
This would also be nice, though I prefer the automated, invisible
approach to one which gets in the way. Which one is more brittle is
also a concern. Is it the spammers write a reverser for your
generator, or akismet falls over under the load or they discover a big
hole in akismet’s algorithms. I think centralized spam detection will
do better but that’s just a hunch.
– G.