Skip to content

Commit 20f3653

Browse files
committed
Define Pathname#sub in C on CRuby for efficiency
* The eval to set $~ is inneficient, so only do it when necessary (when running without the C extension).
1 parent 054a43e commit 20f3653

File tree

2 files changed

+42
-16
lines changed

2 files changed

+42
-16
lines changed

ext/pathname/pathname.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
static VALUE rb_cPathname;
44
static ID id_at_path;
5+
static ID id_sub;
56

67
static VALUE
78
get_strpath(VALUE obj)
@@ -61,6 +62,27 @@ path_cmp(VALUE self, VALUE other)
6162
return INT2FIX(0);
6263
}
6364

65+
/*
66+
* Return a pathname which is substituted by String#sub.
67+
*
68+
* path1 = Pathname.new('/usr/bin/perl')
69+
* path1.sub('perl', 'ruby')
70+
* #=> #<Pathname:/usr/bin/ruby>
71+
*/
72+
static VALUE
73+
path_sub(int argc, VALUE *argv, VALUE self)
74+
{
75+
VALUE str = get_strpath(self);
76+
77+
if (rb_block_given_p()) {
78+
str = rb_block_call(str, id_sub, argc, argv, 0, 0);
79+
}
80+
else {
81+
str = rb_funcallv(str, id_sub, argc, argv);
82+
}
83+
return rb_class_new_instance(1, &str, rb_obj_class(self));
84+
}
85+
6486
static void init_ids(void);
6587

6688
void
@@ -79,11 +101,13 @@ InitVM_pathname(void)
79101
{
80102
rb_cPathname = rb_define_class("Pathname", rb_cObject);
81103
rb_define_method(rb_cPathname, "<=>", path_cmp, 1);
104+
rb_define_method(rb_cPathname, "sub", path_sub, -1);
82105
}
83106

84107
void
85108
init_ids(void)
86109
{
87110
#undef rb_intern
88111
id_at_path = rb_intern("@path");
112+
id_sub = rb_intern("sub");
89113
}

lib/pathname.rb

Lines changed: 18 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -288,23 +288,25 @@ def inspect # :nodoc:
288288
"#<#{self.class}:#{@path}>"
289289
end
290290

291-
# Return a pathname which is substituted by String#sub.
292-
def sub(pattern, *args, **kwargs, &block)
293-
if block
294-
path = @path.sub(pattern, *args, **kwargs) {|*sub_args|
295-
begin
296-
old = Thread.current[:pathname_sub_matchdata]
297-
Thread.current[:pathname_sub_matchdata] = $~
298-
eval("$~ = Thread.current[:pathname_sub_matchdata]", block.binding)
299-
ensure
300-
Thread.current[:pathname_sub_matchdata] = old
301-
end
302-
yield(*sub_args)
303-
}
304-
else
305-
path = @path.sub(pattern, *args, **kwargs)
291+
unless method_defined?(:sub, false)
292+
# Return a pathname which is substituted by String#sub.
293+
def sub(pattern, *args, **kwargs, &block)
294+
if block
295+
path = @path.sub(pattern, *args, **kwargs) {|*sub_args|
296+
begin
297+
old = Thread.current[:pathname_sub_matchdata]
298+
Thread.current[:pathname_sub_matchdata] = $~
299+
eval("$~ = Thread.current[:pathname_sub_matchdata]", block.binding)
300+
ensure
301+
Thread.current[:pathname_sub_matchdata] = old
302+
end
303+
yield(*sub_args)
304+
}
305+
else
306+
path = @path.sub(pattern, *args, **kwargs)
307+
end
308+
self.class.new(path)
306309
end
307-
self.class.new(path)
308310
end
309311

310312
# Return a pathname with +repl+ added as a suffix to the basename.

0 commit comments

Comments
 (0)