]> git.wincent.com - wikitext.git/commitdiff
Allow setting of minimum fulltext token length
authorWincent Colaiuta <win@wincent.com>
Thu, 24 Apr 2008 09:14:16 +0000 (11:14 +0200)
committerWincent Colaiuta <win@wincent.com>
Thu, 24 Apr 2008 09:14:16 +0000 (11:14 +0200)
Provide three means of setting minimum fulltext token length:

- via instance variable (accessors provided)
- via options hash at initialization time (sets instance variable)
- via options hash at tokenization time (overrides instance variable)

Signed-off-by: Wincent Colaiuta <win@wincent.com>
ext/parser.c
ext/parser.h
ext/wikitext.c
spec/fulltext_spec.rb [new file with mode: 0644]

index 25b52fce36c83e4940176244aa0c51b59f26df0c..836e35081f7368018aaa362061870086cd86a7fb 100644 (file)
@@ -158,13 +158,31 @@ VALUE Wikitext_parser_benchmarking_tokenize(VALUE self, VALUE string)
     return Qnil;
 }
 
-VALUE Wikitext_parser_fulltext_tokenize(VALUE self, VALUE string)
+VALUE Wikitext_parser_fulltext_tokenize(int argc, VALUE *argv, VALUE self)
 {
+    // process arguments
+    VALUE string, options;
+    if (rb_scan_args(argc, argv, "11", &string, &options) == 1) // 1 mandatory argument, 1 optional argument
+        options = Qnil;
     if (NIL_P(string))
         return Qnil;
-    long min_len = 3;
     string = StringValue(string);
     VALUE tokens = rb_ary_new();
+
+    // check instance variables
+    VALUE min = rb_iv_get(self, "@minimum_fulltext_token_length");
+
+    // process options hash (can override instance variables)
+    if (!NIL_P(options) && TYPE(options) == T_HASH)
+    {
+        if (rb_funcall(options, rb_intern("has_key?"), 1, ID2SYM(rb_intern("minimum"))) == Qtrue)
+            min = rb_hash_aref(options, ID2SYM(rb_intern("minimum")));
+    }
+    int min_len = NIL_P(min) ? 3 : NUM2INT(min);
+    if (min_len < 0)
+        min_len = 0;
+
+    // set up scanner
     char *p = RSTRING_PTR(string);
     long len = RSTRING_LEN(string);
     char *pe = p + len;
@@ -881,39 +899,42 @@ VALUE Wikitext_parser_initialize(int argc, VALUE *argv, VALUE self)
         options = Qnil;
 
     // defaults
-    VALUE autolink                  = Qtrue;
-    VALUE line_ending               = rb_str_new2("\n");
-    VALUE external_link_class       = rb_str_new2("external");
-    VALUE mailto_class              = rb_str_new2("mailto");
-    VALUE internal_link_prefix      = rb_str_new2("/wiki/");
-    VALUE img_prefix                = rb_str_new2("/images/");
-    VALUE space_to_underscore       = Qfalse;
-    VALUE treat_slash_as_special    = Qtrue;
+    VALUE autolink                      = Qtrue;
+    VALUE line_ending                   = rb_str_new2("\n");
+    VALUE external_link_class           = rb_str_new2("external");
+    VALUE mailto_class                  = rb_str_new2("mailto");
+    VALUE internal_link_prefix          = rb_str_new2("/wiki/");
+    VALUE img_prefix                    = rb_str_new2("/images/");
+    VALUE space_to_underscore           = Qfalse;
+    VALUE treat_slash_as_special        = Qtrue;
+    VALUE minimum_fulltext_token_length = INT2NUM(3);
 
     // process options hash (override defaults)
     if (!NIL_P(options) && TYPE(options) == T_HASH)
     {
 #define OVERRIDE_IF_SET(name)   rb_funcall(options, rb_intern("has_key?"), 1, ID2SYM(rb_intern(#name))) == Qtrue ? \
                                 rb_hash_aref(options, ID2SYM(rb_intern(#name))) : name
-        autolink                = OVERRIDE_IF_SET(autolink);
-        line_ending             = OVERRIDE_IF_SET(line_ending);
-        external_link_class     = OVERRIDE_IF_SET(external_link_class);
-        mailto_class            = OVERRIDE_IF_SET(mailto_class);
-        internal_link_prefix    = OVERRIDE_IF_SET(internal_link_prefix);
-        img_prefix              = OVERRIDE_IF_SET(img_prefix);
-        space_to_underscore     = OVERRIDE_IF_SET(space_to_underscore);
-        treat_slash_as_special  = OVERRIDE_IF_SET(treat_slash_as_special);
+        autolink                        = OVERRIDE_IF_SET(autolink);
+        line_ending                     = OVERRIDE_IF_SET(line_ending);
+        external_link_class             = OVERRIDE_IF_SET(external_link_class);
+        mailto_class                    = OVERRIDE_IF_SET(mailto_class);
+        internal_link_prefix            = OVERRIDE_IF_SET(internal_link_prefix);
+        img_prefix                      = OVERRIDE_IF_SET(img_prefix);
+        space_to_underscore             = OVERRIDE_IF_SET(space_to_underscore);
+        treat_slash_as_special          = OVERRIDE_IF_SET(treat_slash_as_special);
+        minimum_fulltext_token_length   = OVERRIDE_IF_SET(minimum_fulltext_token_length);
     }
 
     // no need to call super here; rb_call_super()
-    rb_iv_set(self, "@autolink",                autolink);
-    rb_iv_set(self, "@line_ending",             line_ending);
-    rb_iv_set(self, "@external_link_class",     external_link_class);
-    rb_iv_set(self, "@mailto_class",            mailto_class);
-    rb_iv_set(self, "@internal_link_prefix",    internal_link_prefix);
-    rb_iv_set(self, "@img_prefix",              img_prefix);
-    rb_iv_set(self, "@space_to_underscore",     space_to_underscore);
-    rb_iv_set(self, "@treat_slash_as_special",  treat_slash_as_special);
+    rb_iv_set(self, "@autolink",                        autolink);
+    rb_iv_set(self, "@line_ending",                     line_ending);
+    rb_iv_set(self, "@external_link_class",             external_link_class);
+    rb_iv_set(self, "@mailto_class",                    mailto_class);
+    rb_iv_set(self, "@internal_link_prefix",            internal_link_prefix);
+    rb_iv_set(self, "@img_prefix",                      img_prefix);
+    rb_iv_set(self, "@space_to_underscore",             space_to_underscore);
+    rb_iv_set(self, "@treat_slash_as_special",          treat_slash_as_special);
+    rb_iv_set(self, "@minimum_fulltext_token_length",   minimum_fulltext_token_length);
     return self;
 }
 
index 969465d67758b2aed7c61bed38f2f31c5488545f..bc6245aab5b23d26e62815f2d9c2e94989d7352a 100644 (file)
@@ -20,7 +20,7 @@ VALUE Wikitext_parser_tokenize(VALUE self, VALUE string);
 
 VALUE Wikitext_parser_benchmarking_tokenize(VALUE self, VALUE string);
 
-VALUE Wikitext_parser_fulltext_tokenize(VALUE self, VALUE string);
+VALUE Wikitext_parser_fulltext_tokenize(int argc, VALUE *argv, VALUE self);
 
 VALUE Wikitext_parser_sanitize_link_target(VALUE self, VALUE string);
 
index 9a5126707b59574ed3d78d0d88f283fb625b10a5..b7136f311a788d271f1adde1c4acefc939379e0d 100644 (file)
@@ -32,7 +32,7 @@ void Init_wikitext()
     rb_define_method(cWikitextParser, "profiling_parse", Wikitext_parser_profiling_parse, 1);
     rb_define_method(cWikitextParser, "tokenize", Wikitext_parser_tokenize, 1);
     rb_define_method(cWikitextParser, "benchmarking_tokenize", Wikitext_parser_benchmarking_tokenize, 1);
-    rb_define_method(cWikitextParser, "fulltext_tokenize", Wikitext_parser_fulltext_tokenize, 1);
+    rb_define_method(cWikitextParser, "fulltext_tokenize", Wikitext_parser_fulltext_tokenize, -1);
     rb_define_singleton_method(cWikitextParser, "sanitize_link_target", Wikitext_parser_sanitize_link_target, 1);
     rb_define_singleton_method(cWikitextParser, "encode_link_target", Wikitext_parser_encode_link_target, 1);
     rb_define_singleton_method(cWikitextParser, "encode_special_link_target", Wikitext_parser_encode_special_link_target, 1);
@@ -44,6 +44,7 @@ void Init_wikitext()
     rb_define_attr(cWikitextParser, "autolink", Qtrue, Qtrue);
     rb_define_attr(cWikitextParser, "treat_slash_as_special", Qtrue, Qtrue);
     rb_define_attr(cWikitextParser, "space_to_underscore", Qtrue, Qtrue);
+    rb_define_attr(cWikitextParser, "minimum_fulltext_token_length", Qtrue, Qtrue);
 
     // Wikitext::Parser::Error
     eWikitextParserError = rb_define_class_under(cWikitextParser, "Error", rb_eException);
diff --git a/spec/fulltext_spec.rb b/spec/fulltext_spec.rb
new file mode 100644 (file)
index 0000000..a4bb905
--- /dev/null
@@ -0,0 +1,91 @@
+#!/usr/bin/env ruby
+# Copyright 2008 Wincent Colaiuta
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+require File.join(File.dirname(__FILE__), 'spec_helper.rb')
+require 'wikitext'
+
+describe Wikitext::Parser, 'fulltext tokenizing' do
+  before do
+    @parser = Wikitext::Parser.new
+  end
+
+  it 'should default to a minimum fulltext token length of 3' do
+    @parser.minimum_fulltext_token_length.should == 3
+  end
+
+  it 'should accept overrides of minimum fulltext token length at initialization time' do
+    parser = Wikitext::Parser.new(:minimum_fulltext_token_length => 10)
+    parser.minimum_fulltext_token_length.should == 10
+    parser.fulltext_tokenize('short loooooooooong').should == ['loooooooooong']
+  end
+
+  it 'should return nil for nil input' do
+    @parser.fulltext_tokenize(nil).should be_nil
+  end
+
+  it 'should return an empty array for empty string input' do
+    @parser.fulltext_tokenize('').should == []
+  end
+
+  it 'should return an empty array for an input string that contains nothing tokenizable' do
+    @parser.fulltext_tokenize('#!?()/&').should == []
+  end
+
+  it 'should tokenize simple words' do
+    @parser.fulltext_tokenize('foo bar baz').should == ['foo', 'bar', 'baz']
+  end
+
+  it 'should omit tokens shorter than the minimum required length' do
+    @parser.fulltext_tokenize('a b baz longer').should == ['baz', 'longer']
+  end
+
+  it 'should accept overrides of minimum length at parse time' do
+    @parser.fulltext_tokenize('a bc baz longer', :minimum => 2).should == ['bc', 'baz', 'longer']
+  end
+
+  it 'should treat a minimum length of 0 as meaning "no minimum length"' do
+    @parser.fulltext_tokenize('a bc baz longer', :minimum => 0).should == ['a', 'bc', 'baz', 'longer']
+  end
+
+  it 'should interpret a minimum length of nil as meaning "default minumum length" (3)' do
+    @parser.minimum_fulltext_token_length = 10
+    @parser.fulltext_tokenize('a bc baz longer', :minimum => nil).should == ['baz', 'longer']
+  end
+
+  it 'should tokenize URLs' do
+    @parser.fulltext_tokenize('foo http://example.com/ bar').should == ['foo', 'http://example.com/', 'bar']
+  end
+
+  it 'should tokenize email addresses' do
+    @parser.fulltext_tokenize('foo user@example.com bar').should == ['foo', 'user@example.com', 'bar']
+  end
+
+  it 'should ignore punctuation' do
+    @parser.fulltext_tokenize("don't forget!").should == ['don', 'forget']
+  end
+
+  it 'should ignore non-ASCII' do
+    # note that a search for "información lingüística" will still work, but might return some false positives
+    @parser.fulltext_tokenize('buscando información lingüística').should == ['buscando', 'informaci', 'ling', 'stica']
+  end
+
+  it 'should ignore wikitext markup' do
+    @parser.fulltext_tokenize("this <nowiki>that</nowiki> [[foo bar]]").should == ['this', 'that', 'foo', 'bar']
+  end
+
+  it 'should tokenize alphanumerics' do
+    @parser.fulltext_tokenize("password99 2008").should == ['password99', '2008']
+  end
+end