Changeset 190f7af639412f6d6b91876cb6b9f609028b7e46

Show
Ignore:
Timestamp:
04/27/08 11:40:39 (8 months ago)
Author:
David Balmain <dbalmain@…>
Parents:
53eafd97a7caf432dce29b625005d7e028ae5b62
Children:
88f53b244ed21fc4833bef05fb376eb4497a2740
git-committer:
David Balmain <dbalmain@gmail.com> / 2008-04-27T11:40:39Z+1000
Message:

Full test coverage for FuzzyQuery?

* Added documentation to FuzzyQuery? source and increased coverage to 100%.
* Also added gcov history plot build files to .gitignore.

Files:
5 modified

Legend:

Unmodified
Added
Removed
  • c/.rake/.gitignore

    r31d0b3 r190f7a  
    33copts 
    44gcov_results.html 
     5gcov_history.jpg 
     6gcov_history.data 
  • c/.rake/gcov_results.erb

    r340bd7 r190f7a  
    2525    <% end %> 
    2626    </table> 
     27    <img src="gcov_history.jpg" alt="Overall gcov coverage results"/> 
    2728    <script type="text/javascript"> 
    2829      var gaJsHost = (("https:" == document.location.protocol) ? "https://ssl." : "http://www."); 
  • c/src/q_fuzzy.c

    r950230 r190f7a  
    1313 ****************************************************************************/ 
    1414 
     15/** 
     16 * Calculate the maximum nomber of allowed edits (or maximum edit distance) 
     17 * for a word to be a match. 
     18 * 
     19 * Note that fuzq->text_len and m are both the lengths text *after* the prefix 
     20 * so `MIN(fuzq->text_len, m) + fuzq->pre_len)` actually gets the byte length 
     21 * of the shorter string out of the query string and the index term being 
     22 * compared. 
     23 */ 
    1524static INLINE int fuzq_calculate_max_distance(FuzzyQuery *fuzq, int m) 
    1625{ 
     
    1827} 
    1928 
     29/** 
     30 * The max-distance formula gets used a lot - it needs to be calculated for 
     31 * every possible match in the index - so we cache the results for all 
     32 * lengths up to the TYPICAL_LONGEST_WORD limit. For words longer than this we 
     33 * calculate the value live. 
     34 */ 
    2035static void fuzq_initialize_max_distances(FuzzyQuery *fuzq) 
    2136{ 
     
    2641} 
    2742 
     43/** 
     44 * Return the cached max-distance value if the word is within the 
     45 * TYPICAL_LONGEST_WORD limit. 
     46 */ 
    2847static INLINE int fuzq_get_max_distance(FuzzyQuery *fuzq, int m) 
    2948{ 
     
    173192    } 
    174193 
    175     if (te == NULL) { 
    176         if (prefix) free(prefix); 
    177         return q; 
    178     } 
     194    assert(NULL != te); 
    179195 
    180196    fuzq->scale_factor = (float)(1.0 / (1.0 - fuzq->min_sim)); 
  • c/test/test_q_fuzzy.c

    r950230 r190f7a  
    1414} 
    1515 
    16 extern void check_hits(TestCase *tc, Searcher *searcher, Query *query, 
    17                        char *expected_hits, char top); 
     16void check_hits(TestCase *tc, Searcher *searcher, Query *query, 
     17                char *expected_hits, char top); 
     18void check_to_s(TestCase *tc, Query *query, Symbol field, char *q_str); 
    1819 
    1920static void do_prefix_test(TestCase *tc, Searcher *searcher, char *qstr, 
     
    5455 
    5556    q = fuzq_new_conf(field, "aaaaa", 0.0, 5, 10); 
    56     q_deref(q); 
    57  
    58     q = fuzq_new(field, "aaaaa"); 
    59     check_hits(tc, sea, q, "0,1,2", -1); 
    60     q_deref(q); 
    61  
     57    check_hits(tc, sea, q, "0", -1); 
     58    q_deref(q); 
     59 
     60    q = fuzq_new(I("not a field"), "aaaaa"); 
     61    check_hits(tc, sea, q, "", -1); 
     62    q_deref(q); 
     63 
     64    /* test prefix length */ 
    6265    do_prefix_test(tc, sea, "aaaaaaaaaaaaaaaaaaaaaa", "8", 1, 0.0); 
    6366    do_prefix_test(tc, sea, "aaaaa", "0,1,2", 0, 0.0); 
     
    6871    do_prefix_test(tc, sea, "aaaaa", "0", 5, 0.0); 
    6972    do_prefix_test(tc, sea, "aaaaa", "0", 6, 0.0); 
    70  
     73    /* test where term will equal prefix but not whole query string */ 
     74    do_prefix_test(tc, sea, "aaaaaaa", "0", 5, 0.0); 
     75 
     76    /* test minimum similarity */ 
     77    do_prefix_test(tc, sea, "aaaaa", "0,1,2,3", 0, 0.2); 
     78    do_prefix_test(tc, sea, "aaaaa", "0,1,2", 1, 0.4); 
     79    do_prefix_test(tc, sea, "aaaaa", "0,1", 1, 0.6); 
     80    do_prefix_test(tc, sea, "aaaaa", "0", 1, 0.8); 
     81 
     82    /* test where no terms will have any similarity */ 
    7183    do_prefix_test(tc, sea, "xxxxx", "", 0, 0.0); 
    7284 
     85    /* test where no terms will have enough similarity to match */ 
    7386    do_prefix_test(tc, sea, "aaccc", "", 0, 0.0); 
    7487 
     88    /* test prefix length but with non-matching term (aaaac does not exit in 
     89     * the index) */ 
    7590    do_prefix_test(tc, sea, "aaaac", "0,1,2", 0, 0.0); 
    7691    do_prefix_test(tc, sea, "aaaac", "0,1,2", 1, 0.0); 
     
    8095    do_prefix_test(tc, sea, "aaaac", "", 5, 0.0); 
    8196 
     97    /* test really long string never matches */ 
    8298    do_prefix_test(tc, sea, "ddddX", "6", 0, 0.0); 
    8399    do_prefix_test(tc, sea, "ddddX", "6", 1, 0.0); 
     
    87103    do_prefix_test(tc, sea, "ddddX", "", 5, 0.0); 
    88104 
     105    /* test non-existing field doesn't break search */ 
    89106    q = fuzq_new_conf(I("anotherfield"), "ddddX", 0.0, 10, 100); 
    90107    top_docs = searcher_search(sea, q, 0, 1, NULL, NULL, NULL); 
     
    151168} 
    152169 
     170/** 
     171 * Test query hashing to to_s functionality 
     172 */ 
    153173static void test_fuzzy_query_hash(TestCase *tc, void *data) 
    154174{ 
     
    156176    (void)data; 
    157177 
    158     q1 = fuzq_new_conf(I("A"), "a", (float)0.4, 2, 100); 
    159     q2 = fuzq_new_conf(I("A"), "a", (float)0.4, 2, 100); 
     178    q1 = fuzq_new_conf(I("A"), "a", 0.4f, 2, 100); 
     179    q2 = fuzq_new_conf(I("A"), "a", 0.4f, 2, 100); 
     180    check_to_s(tc, q1, I("A"), "a~0.4"); 
     181    check_to_s(tc, q1, I("B"), "A:a~0.4"); 
    160182 
    161183    Assert(q_eq(q1, q1), "Test same queries are equal"); 
     
    164186    q_deref(q2); 
    165187 
    166     q2 = fuzq_new_conf(I("A"), "a", (float)0.4, 0, 100); 
     188    q2 = fuzq_new_conf(I("A"), "a", 0.4f, 0, 100); 
    167189    Assert(q_hash(q1) != q_hash(q2), "prelen differs"); 
    168190    Assert(!q_eq(q1, q2), "prelen differs"); 
    169191    q_deref(q2); 
    170192 
    171     q2 = fuzq_new_conf(I("A"), "a", (float)0.5, 2, 100); 
     193    q2 = fuzq_new_conf(I("A"), "a", 0.5f, 2, 100); 
    172194    Assert(q_hash(q1) != q_hash(q2), "similarity differs"); 
    173195    Assert(!q_eq(q1, q2), "similarity differs"); 
    174196    q_deref(q2); 
    175197 
    176     q2 = fuzq_new_conf(I("A"), "b", (float)0.4, 2, 100); 
     198    q2 = fuzq_new_conf(I("A"), "b", 0.4f, 2, 100); 
    177199    Assert(q_hash(q1) != q_hash(q2), "term differs"); 
    178200    Assert(!q_eq(q1, q2), "term differs"); 
    179201    q_deref(q2); 
    180202 
    181     q2 = fuzq_new_conf(I("B"), "a", (float)0.4, 2, 100); 
     203    q2 = fuzq_new_conf(I("B"), "a", 0.4f, 2, 100); 
    182204    Assert(q_hash(q1) != q_hash(q2), "field differs"); 
    183205    Assert(!q_eq(q1, q2), "field differs"); 
     206    q_deref(q2); 
     207 
     208    q2 = fuzq_new_conf(I("field"), "mispell", 0.5f, 2, 100); 
     209    check_to_s(tc, q2, I("field"), "mispell~"); 
     210    check_to_s(tc, q2, I("notfield"), "field:mispell~"); 
    184211    q_deref(q2); 
    185212 
  • ruby/Rakefile

    r0c11a5 r190f7a  
     1$:. << 'lib' 
     2# Some parts of this Rakefile where taken from Jim Weirich's Rakefile for 
     3# Rake. Other parts where taken from the David Heinemeier Hansson's Rails 
     4# Rakefile. Both are under MIT-LICENSE. Thanks to both for their excellent 
     5# projects. 
     6 
    17require 'rake' 
     8require 'rake/testtask' 
     9require 'rake/rdoctask' 
    210require 'rake/clean' 
    3 require 'rake/gempackagetask' 
    4 require 'rake/rdoctask' 
    5 require 'rake/testtask' 
    6  
    7 $:. << 'lib' 
    8 require 'ferret/version' 
    9  
    10  
    11 def say(msg='') 
     11require 'ferret_version' 
     12 
     13begin 
     14  require 'rubygems' 
     15  require 'rake/gempackagetask' 
     16rescue Exception 
     17  nil 
     18end 
     19 
     20CURRENT_VERSION = Ferret::VERSION 
     21if ENV['REL'] 
     22  PKG_VERSION = ENV['REL'] 
     23else 
     24  PKG_VERSION = CURRENT_VERSION 
     25end 
     26 
     27def announce(msg='') 
    1228  STDERR.puts msg 
    1329end 
    1430 
    15 def prompt(msg) 
    16   STDERR.print "#{msg} [Yna]: " 
    17   while true 
    18     case STDIN.gets.chomp! 
    19     when /^(y(es)?)?$/i: return true 
    20     when /^no?$/i: return false 
    21     when /^a(bort)?$/i: fail('aborted') 
    22     else 
    23       STDERR.print "Sorry, I don't understand. Please type y, n or a: " 
    24     end 
    25   end 
    26 end 
    27  
    28 windows = (RUBY_PLATFORM =~ /win/) rescue nil 
    29 SUDO = windows ? "" : "sudo " 
    30  
    31  
    32 task :default => 'test:unit' 
    33 #task :default => :build do 
     31EXT = "ferret_ext.so" 
     32EXT_SRC = FileList["../c/src/*.[c]", "../c/include/*.h", 
     33                   "../c/lib/libstemmer_c/src_c/*.[ch]", 
     34                   "../c/lib/libstemmer_c/runtime/*.[ch]", 
     35                   "../c/lib/libstemmer_c/libstemmer/*.[ch]", 
     36                   "../c/lib/libstemmer_c/include/libstemmer.h"] 
     37EXT_SRC.exclude('../**/ind.[ch]') 
     38 
     39EXT_SRC_DEST = EXT_SRC.map {|fn| File.join("ext", File.basename(fn))} 
     40SRC = (FileList["ext/*.[ch]"] + EXT_SRC_DEST).uniq 
     41 
     42CLEAN.include(FileList['**/*.o', '**/*.obj', 'InstalledFiles', 
     43                       '.config', 'ext/cferret.c']) 
     44CLOBBER.include(FileList['ext/*.so'], 'ext/Makefile', EXT_SRC_DEST) 
     45POLISH = Rake::FileList.new.include(FileList['**/*.so'], 'ext/Makefile') 
     46 
     47desc "Clean specifically for the release." 
     48task :polish => [:clean] do 
     49  POLISH.each { |fn| rm_r fn rescue nil } 
     50end 
     51 
     52desc "Run tests with Valgrind" 
     53task :valgrind do 
     54  sh "valgrind --gen-suppressions=yes --suppressions=ferret_valgrind.supp " + 
     55     "--leak-check=yes --show-reachable=yes -v ruby test/test_all.rb" 
     56  #sh "valgrind --suppressions=ferret_valgrind.supp " + 
     57  #   "--leak-check=yes --show-reachable=yes -v ruby test/unit/index/tc_index_reader.rb" 
     58  #valgrind --gen-suppressions=yes --suppressions=ferret_valgrind.supp --leak-check=yes --show-reachable=yes -v ruby test/test_all.rb 
     59end 
     60 
     61task :default => :test_all 
     62#task :default => :ext do 
    3463#  sh "ruby test/unit/index/tc_index.rb" 
    3564#end 
    3665 
    37   BZLIB_SRC = FileList["../c/lib/bzlib/*.h"] + 
    38               FileList["../c/lib/bzlib/*.c"].map do |fn| 
    39                 fn.gsub(%r{/([^/]*.c)}, '/BZ_\1') 
    40               end 
    41 ############################################################################## 
    42 # Building 
    43 ############################################################################## 
    44  
    45 task :build => 'build:compile' 
    46 namespace :build do 
    47   EXT = "ferret_ext.so" 
    48   # Note: libstemmer.[h] is necessary so that the file isn't included when it 
    49   # doesn't exist. It needs to have one regular expression element. 
    50   EXT_SRC = FileList["../c/src/*.[ch]", "../c/include/*.h", 
    51                      "../c/lib/bzlib/*.[ch]", 
    52                      "../c/lib/libstemmer_c/src_c/*.[ch]", 
    53                      "../c/lib/libstemmer_c/runtime/*.[ch]", 
    54                      "../c/lib/libstemmer_c/libstemmer/*.[ch]", 
    55                      "../c/lib/libstemmer_c/include/libstemmer.[h]"] 
    56   EXT_SRC.exclude('../c/**/ind.[ch]', 
    57                   '../c/**/symbol.[ch]', 
    58                   '../c/include/threading.h', 
    59                   '../c/include/scanner.h', 
    60                   '../c/include/internal.h', 
    61                   '../c/src/lang.c', 
    62                   '../c/include/lang.h') 
    63  
    64   EXT_SRC_MAP = {} 
    65   EXT_SRC_DEST = EXT_SRC.map do |fn| 
    66     ext_fn = File.join("ext", File.basename(fn)) 
    67     if fn =~ /.c$/ and fn =~ /(bzlib|stemmer)/ 
    68       prefix = $1.upcase 
    69       ext_fn.gsub!(/ext\//, "ext/#{prefix}_") 
    70     end 
    71     EXT_SRC_MAP[fn] = ext_fn 
    72   end 
    73   SRC = FileList["ext/*.[ch]", EXT_SRC_DEST, 'ext/internal.h'].uniq 
    74  
    75   CLEAN.include   ['**/*.o', '**/*.obj', '.config', 'ext/cferret.c'] 
    76   CLOBBER.include ['doc/api', 'ext/*.so', 'ext/Makefile', 
    77                    'ext/internal.h', EXT_SRC_DEST] 
    78  
    79   # The following block creates file tasks for all of the c files. They 
    80   # belong in the ../c directory in source the working copy and they need 
    81   # to be linked to in the ext directory 
    82   EXT_SRC.each do |fn| 
    83     dest_fn = EXT_SRC_MAP[fn] 
    84     # prepend lib files to avoid conflicts 
    85     file dest_fn => fn do |t| 
    86       ln fn, dest_fn 
    87  
    88       if fn =~ /stemmer/ 
    89         # flatten the directory structure for lib_stemmer 
    90         open(dest_fn) do |in_f| 
    91           open(dest_fn + ".out", "w") do |out_f| 
    92             in_f.each do |line| 
    93               out_f.write(line.sub(/(#include ["<])[.a-z_\/]*\//, '\1')) 
    94             end 
    95           end 
     66desc "Run all tests" 
     67task :test_all => [ :test_units ] 
     68 
     69desc "Generate API documentation" 
     70task :doc => [ :appdoc ] 
     71 
     72desc "run unit tests in test/unit" 
     73Rake::TestTask.new("test_units" => :ext) do |t| 
     74  t.libs << "test/unit" 
     75  t.pattern = 'test/unit/t[cs]_*.rb' 
     76  #t.pattern = 'test/unit/search/tc_index_searcher.rb' 
     77  t.verbose = true 
     78end 
     79 
     80desc "Generate documentation for the application" 
     81rd = Rake::RDocTask.new("appdoc") do |rdoc| 
     82  rdoc.rdoc_dir = 'doc/api' 
     83  rdoc.title    = "Ferret Search Library Documentation" 
     84  rdoc.options << '--line-numbers' 
     85  rdoc.options << '--inline-source' 
     86  rdoc.options << '--charset=utf-8' 
     87  rdoc.rdoc_files.include('README') 
     88  rdoc.rdoc_files.include('TODO') 
     89  rdoc.rdoc_files.include('TUTORIAL') 
     90  rdoc.rdoc_files.include('MIT-LICENSE') 
     91  rdoc.rdoc_files.include('lib/**/*.rb') 
     92  rdoc.rdoc_files.include('ext/r_*.c') 
     93  rdoc.rdoc_files.include('ext/ferret.c') 
     94end 
     95 
     96EXT_SRC.each do |fn| 
     97  dest_fn = File.join("ext", File.basename(fn)) 
     98  file dest_fn => fn do |t| 
     99    begin 
     100      raise "copy for release" if ENV["REL"] 
     101      ln_s File.join("..", fn), dest_fn 
     102    rescue Exception => e 
     103      cp File.expand_path(fn), dest_fn 
     104    end 
     105 
     106    if fn =~ /stemmer/ 
     107      # flatten the directory structure for lib_stemmer 
     108      open(dest_fn) do |in_f| 
     109        open(dest_fn + ".out", "w") do |out_f| 
     110          in_f.each {|line| out_f.write(line.sub(/(#include ["<])[.a-z_\/]*\//) {"#{$1}"})} 
    96111        end 
    97         mv dest_fn + ".out", dest_fn 
    98112      end 
    99     end 
    100   end if File.exists?("../c") 
    101  
    102   file 'ext/internal.h' => '../c/include/internal.h' do 
    103     File.open('ext/internal.h', 'w') do |f| 
    104       File.readlines('../c/include/internal.h').each do |l| 
    105         next if l =~ /ALLOC/ and l !~ /ZERO|MP_/ 
    106         f.puts(l) 
    107       end 
    108     end 
    109   end 
    110  
    111   desc "Build the extension (ferret_ext.so). You'll need a C compiler and Make." 
    112   task :compile => ["ext/#{EXT}"] + SRC 
    113  
    114   file "ext/#{EXT}" => "ext/Makefile" do 
    115     cd "ext" 
    116     if windows and ENV['make'].nil? 
    117       begin 
    118         sh "nmake" 
    119       rescue Exception => e 
    120         path = ':\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT' 
    121         if File.exists? "f#{path}" 
    122           sh "f#{path}" 
    123         elsif File.exists? "c#{path}" 
    124           sh "c#{path}" 
    125         else 
    126           say 
    127           say "***************************************************************" 
    128           say "You need to have Visual C++ 6 to build Ferret on Windows." 
    129           say "If you have it installed, you may need to run;" 
    130           say ' C:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT' 
    131           say "***************************************************************" 
    132           say 
    133           raise e 
    134         end 
    135         sh "nmake" 
    136       end 
    137     else 
    138       sh "make" 
    139     end 
    140     cd ".." 
    141   end 
    142  
    143   file "ext/Makefile" => SRC do 
    144     cd "ext" 
    145     `ruby extconf.rb` 
    146     cd ".." 
    147   end 
    148 end 
    149  
    150 ############################################################################## 
    151 # Testing 
    152 ############################################################################## 
    153  
    154 task :test => 'test:units' 
    155 namespace :test do 
    156   desc "Run tests with Valgrind" 
    157   task :valgrind do 
    158     sh "valgrind --suppressions=ferret_valgrind.supp " + 
    159        "--leak-check=yes --show-reachable=yes " + 
    160        "-v ruby test/unit/index/tc_index_reader.rb" 
    161   end 
    162  
    163   desc "Run all tests" 
    164   task :all => [ :units ] 
    165  
    166   desc "run unit tests in test/unit" 
    167   Rake::TestTask.new("units" => :build) do |t| 
    168     t.libs << "test/unit" 
    169     t.pattern = 'test/unit/t[cs]_*.rb' 
    170     t.verbose = true 
    171   end 
    172   task :unit => :units 
    173  
    174   desc "run tests using locally installed gem" 
    175   Rake::TestTask.new("installed") do |t| 
    176     t.libs << "test/unit" 
    177     t.ruby_opts << '-rtest/test_installed' 
    178     t.pattern = 'test/unit/t[cs]_*.rb' 
    179     t.verbose = true 
    180   end 
    181 end 
    182  
    183 ############################################################################## 
    184 # Documentation 
    185 ############################################################################## 
    186  
    187 desc "Generate API documentation" 
    188 task :doc => 'doc:rdoc' 
    189 namespace :doc do 
    190   if allison = Gem.cache.search('allison').last 
    191     allison_template = File.join(allison.full_gem_path, 'lib/allison.rb') 
    192   end 
    193   desc "Generate documentation for the application" 
    194   $rd = Rake::RDocTask.new do |rdoc| 
    195     rdoc.rdoc_dir = 'doc/api' 
    196     rdoc.title    = "Ferret Search Library Documentation" 
    197     rdoc.options << '--line-numbers' 
    198     rdoc.options << '--inline-source' 
    199     rdoc.options << '--charset=utf-8' 
    200     rdoc.template = allison_template if allison_template 
    201     rdoc.rdoc_files.include('README') 
    202     rdoc.rdoc_files.include('TODO') 
    203     rdoc.rdoc_files.include('TUTORIAL') 
    204     rdoc.rdoc_files.include('MIT-LICENSE') 
    205     rdoc.rdoc_files.include('lib/**/*.rb') 
    206     rdoc.rdoc_files.include('ext/r_*.c') 
    207     rdoc.rdoc_files.include('ext/ferret.c') 
    208   end 
    209  
    210   desc "Look for TODO and FIXME tags in the code" 
    211   task :todo do 
    212     FileList['**/*.rb', 'ext/*.[ch]'].egrep /[#*].*(FIXME|TODO|TBD)/i 
    213   end 
    214 end 
    215  
    216 ############################################################################## 
    217 # Packaging and Installing 
    218 ############################################################################## 
     113      mv dest_fn + ".out", dest_fn 
     114    end 
     115  end 
     116end if File.exists?("../c") 
     117 
     118desc "Build the extension" 
     119task :ext => ["ext/#{EXT}"] + SRC do 
     120  rm_f 'ext/mem_pool.*' 
     121  rm_f 'ext/defines.h' 
     122end 
     123 
     124file "ext/#{EXT}" => ["ext/Makefile"] do 
     125  cp "ext/inc/lang.h", "ext/lang.h" 
     126  cp "ext/inc/threading.h", "ext/threading.h" 
     127  cd "ext" 
     128  if (/mswin/ =~ RUBY_PLATFORM) and ENV['make'].nil? 
     129    begin 
     130      sh "nmake" 
     131    rescue Exception => e 
     132      puts 
     133      puts "**********************************************************************" 
     134      puts "You may need to call VCVARS32.BAT to set the environment variables." 
     135      puts '  "f:\Program Files\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"' 
     136      puts "**********************************************************************" 
     137      puts 
     138      raise e 
     139    end 
     140  else 
     141    sh "make" 
     142  end 
     143  cd ".." 
     144end 
     145 
     146file "ext/lang.h" => ["ext/inc/lang.h"] do 
     147  rm_f "ext/lang.h" 
     148  cp "ext/inc/lang.h", "ext/lang.h" 
     149end 
     150 
     151file "ext/threading.h" => ["ext/inc/threading.h"] do 
     152  rm_f "ext/threading.h" 
     153  cp "ext/inc/threading.h", "ext/threading.h" 
     154end 
     155 
     156file "ext/Makefile" => SRC do 
     157  cd "ext" 
     158  `ruby extconf.rb` 
     159  cd ".." 
     160end 
     161 
     162# Make Parsers --------------------------------------------------------------- 
     163 
     164RACC_SRC = FileList["lib/**/*.y"] 
     165RACC_OUT = RACC_SRC.collect { |fn| fn.sub(/\.y$/, '.tab.rb') } 
     166 
     167task :parsers => RACC_OUT 
     168rule(/\.tab\.rb$/ => [proc {|tn| tn.sub(/\.tab\.rb$/, '.y')}]) do |t| 
     169  sh "racc #{t.source}"  
     170end 
     171 
     172# Create Packages ------------------------------------------------------------ 
    219173 
    220174PKG_FILES = FileList[ 
    221175  'setup.rb', 
    222176  '[-A-Z]*', 
     177  'ext/**/*.[ch]',  
    223178  'lib/**/*.rb',  
    224179  'lib/**/*.rhtml',  
     
    228183  'test/**/wordfile', 
    229184  'rake_utils/**/*.rb', 
    230   'Rakefile', 
    231   SRC 
     185  'Rakefile' 
    232186] 
    233  
    234 spec = Gem::Specification.new do |s| 
     187PKG_FILES.exclude('**/*.o') 
     188PKG_FILES.exclude('**/Makefile') 
     189PKG_FILES.exclude('ext/ferret_ext.so') 
     190 
     191 
     192if ! defined?(Gem) 
     193  puts "Package Target requires RubyGEMs" 
     194else 
     195  spec = Gem::Specification.new do |s| 
     196     
     197    #### Basic information. 
     198    s.name = 'ferret' 
     199    s.version = PKG_VERSION 
     200    s.summary = "Ruby indexing library." 
     201    s.description = <<-EOF 
     202      Ferret is a port of the Java Lucene project. It is a powerful 
     203      indexing and search library. 
     204    EOF 
     205 
     206    #### Dependencies and requirements. 
     207    s.add_dependency('rake') 
     208    s.files = PKG_FILES.to_a 
     209    s.extensions << "ext/extconf.rb" 
     210    s.require_path = 'lib' 
     211    s.autorequire = 'ferret' 
     212    s.bindir = 'bin' 
     213    s.executables = ['ferret-browser'] 
     214    s.default_executable = 'ferret-browser' 
     215 
     216    #### Author and project details. 
     217    s.author = "David Balmain" 
     218    s.email = "dbalmain@gmail.com" 
     219    s.homepage = "http://ferret.davebalmain.com/trac" 
     220    s.rubyforge_project = "ferret" 
     221 
     222    s.has_rdoc = true 
     223    s.extra_rdoc_files = rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a 
     224    s.rdoc_options << 
     225      '--title' <<  'Ferret -- Ruby Indexer' << 
     226      '--main' << 'README' << '--line-numbers' << 
     227      'TUTORIAL' << 'TODO' 
     228 
     229    if RUBY_PLATFORM =~ /mswin/ 
     230      s.files = PKG_FILES.to_a + ["ext/#{EXT}"] 
     231      s.extensions.clear 
     232      s.platform = Gem::Platform::WIN32 
     233    end 
     234  end 
     235 
     236  package_task = Rake::GemPackageTask.new(spec) do |pkg| 
     237    unless RUBY_PLATFORM =~ /mswin/ 
     238      pkg.need_zip = true 
     239      pkg.need_tar = true 
     240    end 
     241  end 
     242end 
     243 
     244# Support Tasks ------------------------------------------------------ 
     245 
     246desc "Look for TODO and FIXME tags in the code" 
     247task :todo do 
     248  FileList['**/*.rb'].egrep /#.*(FIXME|TODO|TBD)/ 
     249end 
     250# -------------------------------------------------------------------- 
     251# Creating a release 
     252 
     253desc "Make a new release" 
     254task :release => [ 
     255  :prerelease, 
     256  :polish, 
     257  :test_all, 
     258  :update_version, 
     259  :package, 
     260  :tag] do 
     261  announce  
     262  announce "**************************************************************" 
     263  announce "* Release #{PKG_VERSION} Complete." 
     264  announce "* Packages ready to upload." 
     265  announce "**************************************************************" 
     266  announce  
     267end 
     268 
     269# Validate that everything is ready to go for a release. 
     270task :prerelease do 
     271  announce  
     272  announce "**************************************************************" 
     273  announce "* Making RubyGem Release #{PKG_VERSION}" 
     274  announce "* (current version #{CURRENT_VERSION})" 
     275  announce "**************************************************************" 
     276  announce   
     277 
     278  # Is a release number supplied? 
     279  unless ENV['REL'] 
     280    fail "Usage: rake release REL=x.y.z [REUSE=tag_suffix]" 
     281  end 
     282 
     283  # Is the release different than the current release. 
     284  # (or is REUSE set?) 
     285  if PKG_VERSION == CURRENT_VERSION && ! ENV['REUSE'] 
     286    fail "Current version is #{PKG_VERSION}, must specify REUSE=tag_suffix to reuse version" 
     287  end 
     288 
     289  # Are all source files checked in? 
     290  data = `svn -q --ignore-externals status` 
     291  unless data =~ /^$/ 
     292    fail "'svn -q status' is not clean ... do you have unchecked-in files?" 
     293  end 
    235294   
    236   #### Basic information. 
    237   s.name = 'ferret' 
    238   s.version = Ferret::VERSION 
    239   s.summary = "Ruby indexing library." 
    240   s.description = "Ferret is a super fast, highly configurable search library." 
    241  
    242   #### Dependencies and requirements. 
    243   s.add_dependency('rake') 
    244   s.files = PKG_FILES.to_a 
    245   s.extensions << "ext/extconf.rb" 
    246   s.require_path = 'lib' 
    247   s.bindir = 'bin' 
    248   s.executables = ['ferret-browser'] 
    249   s.default_executable = 'ferret-browser' 
    250  
    251   #### Author and project details. 
    252   s.author = "David Balmain" 
    253   s.email = "dbalmain@gmail.com" 
    254   s.homepage = "http://ferret.davebalmain.com/trac" 
    255   s.rubyforge_project = "ferret" 
    256  
    257   s.has_rdoc = true 
    258   s.extra_rdoc_files = $rd.rdoc_files.reject { |fn| fn =~ /\.rb$/ }.to_a 
    259   s.rdoc_options << 
    260     '--title' <<  'Ferret -- Ruby Search Library' << 
    261     '--main' << 'README' << '--line-numbers' << 
    262     'TUTORIAL' << 'TODO' 
    263  
    264   key_file = File.expand_path('~/.gem/gem-private_key.pem') 
    265   key_file = nil unless File.exists?(key_file) 
    266   cert_file = File.expand_path('~/.gem/gem-public_cert.pem') 
    267   cert_file = nil unless File.exists?(cert_file) 
    268   if key_file and cert_file 
    269     s.signing_key = key_file 
    270     s.cert_chain  = cert_file 
    271