Permalink
Browse files

Word suggestions.

The Speller class is now able to generate an array of suggested words based on
a specified word.

Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
  • Loading branch information...
1 parent bf0809f commit ad0f2998818d07e6a89d31705a97ffbc293e86dc @YorickPeterse committed Apr 18, 2012
Showing with 112 additions and 19 deletions.
  1. +15 −0 lib/ffi/aspell.rb
  2. +26 −2 lib/ffi/aspell/speller.rb
  3. +45 −0 spec/ffi/aspell/suggestions.rb
  4. +25 −16 task/memory.rake
  5. +1 −1 task/test.rake
View
@@ -64,10 +64,25 @@ module Aspell
[:pointer, :string, :int],
:bool
+ # Functions for dealing with suggestions.
attach_function 'speller_suggest',
'aspell_speller_suggest',
[:pointer, :string, :int],
:pointer
+ attach_function 'word_list_elements',
+ 'aspell_word_list_elements',
+ [:pointer],
+ :pointer
+
+ attach_function 'string_enumeration_delete',
+ 'delete_aspell_string_enumeration',
+ [:pointer],
+ :void
+
+ attach_function 'string_enumeration_next',
+ 'aspell_string_enumeration_next',
+ [:pointer],
+ :string
end # Aspell
end # FFI
View
@@ -4,10 +4,21 @@ module Aspell
# The Speller class is used for spell checking individual words as well as
# generating a list of suggestions.
#
+ # TODO: currently this class creates a new speller object every time
+ # {FFI::Aspell::Speller#correct?} and similar methods are called. I'm not
+ # entirely sure if this is needed, if not it should be modified.
+ #
# @since 13-04-2012
#
class Speller
##
+ # Array containing the possible suggestion modes to use.
+ #
+ # @since 18-04-2012
+ #
+ SUGGESTION_MODES = ['ultra', 'fast', 'normal', 'bad-spellers']
+
+ ##
# Creates a new instance of the class, sets the language as well as the
# options specified in the `options` hash.
#
@@ -54,9 +65,18 @@ def correct?(word)
#
def suggestions(word)
speller = Aspell.speller_new(@config)
- suggestions = Aspell.speller_suggest(speller, word, word.length)
+ list = Aspell.speller_suggest(speller, word, word.length)
+ suggestions = []
+ elements = Aspell.word_list_elements(list)
+ while word = Aspell.string_enumeration_next(elements)
+ suggestions << word
+ end
+
+ Aspell.string_enumeration_delete(elements)
Aspell.speller_delete(speller)
+
+ return suggestions
end
##
@@ -86,7 +106,7 @@ def suggestion_mode
# @param [#to_s] key The configuration key to set.
# @param [#to_s] value The value of the configuration key.
# @raise [FFI::Aspell::ConfigError] Raised when the configuration value
- # could not be set.
+ # could not be set or when an incorrect suggestion mode was given.
#
def set(key, value)
unless key.respond_to?(:to_s)
@@ -97,6 +117,10 @@ def set(key, value)
raise(TypeError, 'Configuration values should respond to #to_s()')
end
+ if key == 'sug-mode' and !SUGGESTION_MODES.include?(value)
+ raise(ConfigError, "The suggestion mode #{value} is invalid")
+ end
+
unless Aspell.config_replace(@config, key.to_s, value.to_s)
raise(ConfigError, "Failed to set the configuration item #{key}")
end
@@ -0,0 +1,45 @@
+require File.expand_path('../../../helper', __FILE__)
+
+describe 'FFI::Aspell::Speller#suggestions' do
+ it 'Return a list of word suggestions using the default mode' do
+ speller = FFI::Aspell::Speller.new
+ suggestions = speller.suggestions('cookei')
+
+ suggestions.include?('coke').should == true
+ suggestions.include?('cookie').should == true
+ suggestions.include?('cooked').should == true
+ end
+
+ it 'Return a list of word suggestions using the "bad-spellers" mode' do
+ speller = FFI::Aspell::Speller.new
+
+ # Get the amount of suggestions for the normal mode. The "bad-spellers" mode
+ # should result in a lot more possible suggestions.
+ normal_length = speller.suggestions('cookei').length
+
+ speller.suggestion_mode = 'bad-spellers'
+ suggestions = speller.suggestions('cookei')
+
+ suggestions.include?('coke').should == true
+ suggestions.include?('cookie').should == true
+ suggestions.include?('cooked').should == true
+
+ suggestions.length.should > normal_length
+ end
+
+ it 'Raise an error when an invalid suggestion mode is used' do
+ speller = FFI::Aspell::Speller.new
+
+ should.raise(FFI::Aspell::ConfigError) do
+ speller.suggestion_mode = 'does-not-exist'
+ end
+
+ speller.suggestion_mode.should == 'normal'
+
+ should.not.raise(FFI::Aspell::ConfigError) do
+ speller.suggestion_mode = 'ultra'
+ end
+
+ speller.suggestion_mode.should == 'ultra'
+ end
+end
View
@@ -1,41 +1,50 @@
+# Cheap way of benchmarking the memory usage of various parts of the FFI
+# binding.
+def benchmark_block(amount = 10000)
+ require File.expand_path('../../lib/ffi/aspell', __FILE__)
+
+ start_mem = `ps -o rss= #{Process.pid}`.to_f
+
+ amount.times { yield }
+
+ mem = ((`ps -o rss= #{Process.pid}`.to_f - start_mem) / 1024).round(2)
+
+ puts "Memory increase in Megabytes: #{mem} MB"
+end
+
namespace :memory do
memory = proc { `ps -o rss= #{Process.pid}`.to_i }
desc 'Show memory usage of Aspell.speller_new'
task :speller, [:amount] do |task, args|
args.with_defaults(:amount => 10000)
- require File.expand_path('../../lib/ffi/aspell', __FILE__)
-
- start_mem = memory.call
-
- args.amount.times do
+ benchmark_block(args.amount) do
speller = FFI::Aspell.speller_new(FFI::Aspell.config_new)
FFI::Aspell.speller_delete(speller)
end
-
- mem = (memory.call - start_mem) / 1024
-
- puts "Memory usage in Megabytes: #{mem} MB"
end
desc 'Show memory usage of Speller#correct?'
task :correct, [:amount] do |task, args|
args.with_defaults(:amount => 10000)
- require File.expand_path('../../lib/ffi/aspell', __FILE__)
-
- start_mem = memory.call
-
- args.amount.times do
+ benchmark_block(args.amount) do
speller = FFI::Aspell::Speller.new
speller.correct?('cookie')
end
+ end
- mem = (memory.call - start_mem) / 1024
+ desc 'Show memory usage of Speller#suggestions'
+ task :suggestions, [:amount] do |task, args|
+ args.with_defaults(:amount => 10000)
+
+ benchmark_block(args.amount) do
+ speller = FFI::Aspell::Speller.new
- puts "Memory usage in Megabytes: #{mem} MB"
+ speller.suggestions('cookei')
+ end
end
end
View
@@ -1,4 +1,4 @@
desc 'Runs all the tests using Bacon'
task :test do
- Dir['./spec/ffi/aspell/**/*.rb'].each { |spec| require(spec) }
+ Dir['./spec/ffi/aspell/**/*.rb'].sort.each { |spec| require(spec) }
end

0 comments on commit ad0f299

Please sign in to comment.