Hi all, After a recent "idiomatic" thread, I decided to try and refactor the core File.basename method. I think I did a pretty good job. It's about 10 lines short, easier to read (I think), and self contained, i.e. no calls to rmext(). However it is a *tad* slower, but not by much. I was wondering if anyone would help me tweak it. Below is the (self contained) code, extconf.rb file, a test suite (taken from Rubicon - it passes all tests) and a benchmark program. Any and all help appreciated. Regards, Dan PS - Note that I do not expect this code to work on Windows, for which I use a completely different approach using their builtin path handling functions. /* base.c */ #include "ruby.h" #include <stdio.h> #include <strings.h> #include <libgen.h> static VALUE base_basename(int argc, VALUE* argv, VALUE klass){ VALUE rbFileName, rbExt, rbBase; rb_scan_args(argc, argv, "11", &rbFileName, &rbExt); StringValue(rbFileName); if(RSTRING(rbFileName)->len == 0) /* edge case */ rbBase = rbFileName; else rbBase = rb_str_new2(basename(StringValuePtr(rbFileName))); if(RTEST(rbExt)){ char* base = RSTRING(rbBase)->ptr; /* for readability */ char* ext = RSTRING(rbExt)->ptr; /* ditto */ if(!strcmp(ext, ".*")){ int length = strlen(base) - strlen(strrchr(base, '.')); rbBase = rb_str_new(base, length); } else{ if(strstr(base, ext)){ int length = strlen(base) - strlen(ext); int span = strcspn(base, ext); if(length == span) rbBase = rb_str_new(base, length); } } } return rbBase; } void Init_base(){ VALUE rbBase = rb_define_class("Base", rb_cObject); rb_define_singleton_method(rbBase, "basename", base_basename, -1); } # extconf.rb require "mkmf" create_makefile("base") # test.rb - borrowed asserts from Rubicon project $:.unshift(Dir.pwd) require "base" require "test/unit" class TC_Base < Test::Unit::TestCase def setup @file = File.join("_test", "_touched") end def test_basename assert_equal("_touched", File.basename(@file)) assert_equal("tmp", File.basename(File.join("/tmp"))) assert_equal("b", File.basename(File.join(*%w( g f d s a b)))) assert_equal("tmp", File.basename("/tmp", ".*")) assert_equal("tmp", File.basename("/tmp", ".c")) assert_equal("tmp", File.basename("/tmp.c", ".c")) assert_equal("tmp", File.basename("/tmp.c", ".*")) assert_equal("tmp.o", File.basename("/tmp.o", ".c")) assert_equal("tmp", File.basename(File.join("/tmp/"))) assert_equal("/", File.basename("/")) assert_equal("/", File.basename("//")) assert_equal("base", File.basename("dir///base", ".*")) assert_equal("base", File.basename("dir///base", ".c")) assert_equal("base", File.basename("dir///base.c", ".c")) assert_equal("base", File.basename("dir///base.c", ".*")) assert_equal("base.o", File.basename("dir///base.o", ".c")) assert_equal("base", File.basename("dir///base///")) assert_equal("base", File.basename("dir//base/", ".*")) assert_equal("base", File.basename("dir//base/", ".c")) assert_equal("base", File.basename("dir//base.c/", ".c")) assert_equal("base", File.basename("dir//base.c/", ".*")) assert_equal("base.o", File.basename("dir//base.o/", ".c")) end def teardown @file = nil end end # basebench.rb $:.unshift(Dir.pwd) require "base" require "benchmark" include Benchmark MAX = 200000 bm do |x| x.report("Old #1"){ MAX.times{ File.basename("/foo/bar/baz.rb") } } x.report("New #1"){ MAX.times{ Base.basename("/foo/bar/baz.rb") } } puts x.report("Old #2"){ MAX.times{ File.basename("/foo/bar/baz.rb",".rb") } } x.report("New #2"){ MAX.times{ Base.basename("/foo/bar/baz.rb",".rb") } } puts x.report("Old #3"){ MAX.times{ File.basename("foo.rb", ".rb") } } x.report("New #3"){ MAX.times{ Base.basename("foo.rb", ".rb") } } puts x.report("Old #4"){ MAX.times{ File.basename("foo.rb", ".py") } } x.report("New #4"){ MAX.times{ Base.basename("foo.rb", ".py") } } puts x.report("Old #5"){ MAX.times{ File.basename("foo.rb.py", ".rb") } } x.report("New #5"){ MAX.times{ Base.basename("foo.rb.py", ".rb") } } end # Current results on Sunblade 150, Solaris 10, 512 MB RAM, USIIe 650 MHz user system total real Old #1 1.990000 0.870000 2.860000 ( 2.963725) New #1 2.010000 0.880000 2.890000 ( 2.981809) Old #2 2.280000 0.880000 3.160000 ( 3.279448) New #2 2.360000 0.880000 3.240000 ( 3.364783) Old #3 2.210000 0.880000 3.090000 ( 3.271177) New #3 2.660000 0.890000 3.550000 ( 3.685172) Old #4 1.710000 0.850000 2.560000 ( 2.646357) New #4 2.250000 0.880000 3.130000 ( 3.253422) Old #5 1.690000 0.850000 2.540000 ( 2.652825) New #5 2.380000 0.890000 3.270000 ( 3.417572)