What is the correct way to extend native methods?

Hello!

suppose I want to modify Array.slice() (and consequentially, Array[]) in
order to accept a RegExp as argument, and return values matching the
regular expression. I tried to solve this problem in this way:

class Array

  def slice_with_regexp(*args)
    puts args
    return self.select{ |val| val.to_s =~ args[0] } if args.size == 1
and args[0].is_a?(RegExp)
  end

  alias_method :slice_without_regexp, :slice
  alias_method :slice, :slice_with_regexp

end

arr = %w[a b c]
puts arr.slice(/a/)

but executing this script I get this error:

(?-mix:a)
scripts.rb:8:in `slice_with_regexp': uninitialized constant RegExp
(NameError)
  from scripts.rb:17:in `<main>'

Surely I did something wrong, but the very question is: what is the
correct way to get the desired effect (that is:)

arr = %w[a b c]
puts arr[/a/]
=> ["a"]
puts arr.slice(/a/)
=> ["a"]

?

···

--
Posted via http://www.ruby-forum.com/.

   return self.select{ |val| val.to_s =~ args[0] } if args.size == 1
and args[0].is_a?(RegExp)

Try Regexp, not RegExp

http://ruby-doc.org/core/classes/Regexp.html

DON'T!

It's almost always a bad idea to override a built-in core method.
There are likely to be unintended effects that will break other code.
The best approach is to define a new method and use that.

···

On Jul 31, 9:19 pm, Maurizio De Santis <desantis.mauri...@gmail.com> wrote:

Hello!

suppose I want to modify Array.slice() (and consequentially, Array) in
order to accept a RegExp as argument, and return values matching the
regular expression. I tried to solve this problem in this way:

James Harrison wrote:

   return self.select{ |val| val.to_s =~ args[0] } if args.size == 1
and args[0].is_a?(RegExp)

Try Regexp, not RegExp

http://ruby-doc.org/core/classes/Regexp.html

LOL that's what I obtain when I'm still not asleep at 3.00 am :stuck_out_tongue:

But now it's morning, and I have still a problem; please consider this:

class Array

  def slice_with_regexp(*args)
    return self.select{ |val| val =~ args[0] } if args.size == 1 and
args[0].is_a?(Regexp)
    slice_without_regexp(*args)
  end

  alias_method :slice_without_regexp, :slice
  alias_method :slice_without_regexp, :
  alias_method :slice, :slice_with_regexp

end

arr = %w[a b c ab]
puts arr.slice(/a/).inspect # => ["a", "ab"] slice with Regexp works;
correct
puts arr.slice(1).inspect # => "b" original slice still works; correct
puts arr[/a/].inspect # => stack level too deep ???

why??

···

--
Posted via http://www.ruby-forum.com/\.

Maurizio De Santis wrote:

why??

What ruby are you using? With ruby 1.8.7 (2010-01-10 patchlevel 249)
[x86_64-linux] and your posted code, I get the following output:

["a", "ab"]
"b"
ert.rb:17:in `': can't convert Regexp into Integer (TypeError)
  from ert.rb:17

If I change it to

  alias_method :slice_without_regexp, :slice
  alias_method :slice, :slice_with_regexp
  alias_method :, :slice_with_regexp

then *that* gives a stack too deep error, but on the initial slice call.

I'm afraid I don't know the solution though. I suspect slice is already
an alias for , or vice versa.

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

···

On Sun, 1 Aug 2010, Brian Candler wrote:

Maurizio De Santis wrote:

why??

What ruby are you using? With ruby 1.8.7 (2010-01-10 patchlevel 249)
[x86_64-linux] and your posted code, I get the following output:

["a", "ab"]
"b"
ert.rb:17:in `': can't convert Regexp into Integer (TypeError)
from ert.rb:17

If I change it to

alias_method :slice_without_regexp, :slice
alias_method :slice, :slice_with_regexp
alias_method :, :slice_with_regexp

then *that* gives a stack too deep error, but on the initial slice call.

I'm afraid I don't know the solution though. I suspect slice is already
an alias for , or vice versa.

They are indeed the same method:

     rb_define_method(rb_cArray, "", rb_ary_aref, -1);
     rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);

David

--
David A. Black, Senior Developer, Cyrus Innovation Inc.

   The Ruby training with Black/Brown/McAnally
   Compleat Philadelphia, PA, October 1-2, 2010
   Rubyist http://www.compleatrubyist.com

Brian Candler wrote:

What ruby are you using?

I'm using ruby 1.9.2dev (2010-07-11 revision 28613) [x86_64-linux] ,
maybe this is the reason...

David A. Black wrote:

They are indeed the same method:

     rb_define_method(rb_cArray, "", rb_ary_aref, -1);
     rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);

uhm... but if they are the same method, why aliasing the one, the other
does not become aliased? do I have to alias both methods?? and, if yes,
how??? :slight_smile: I've tried, but with no success...

···

--
Posted via http://www.ruby-forum.com/\.

David A. Black wrote:

Hi --

Maurizio De Santis wrote:

why??

What ruby are you using? With ruby 1.8.7 (2010-01-10 patchlevel 249)
[x86_64-linux] and your posted code, I get the following output:

["a", "ab"]
"b"
ert.rb:17:in `': can't convert Regexp into Integer (TypeError)
from ert.rb:17

If I change it to

alias_method :slice_without_regexp, :slice
alias_method :slice, :slice_with_regexp
alias_method :, :slice_with_regexp

then *that* gives a stack too deep error, but on the initial slice call.

I'm afraid I don't know the solution though. I suspect slice is already
an alias for , or vice versa.

They are indeed the same method:

    rb_define_method(rb_cArray, "", rb_ary_aref, -1);
    rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);

Though, technically they're synonyms, not aliases. :slight_smile:

http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-core/13301

Regards,

Dan

···

On Sun, 1 Aug 2010, Brian Candler wrote:

Hi --

···

On Sun, 1 Aug 2010, Maurizio De Santis wrote:

Brian Candler wrote:

What ruby are you using?

I'm using ruby 1.9.2dev (2010-07-11 revision 28613) [x86_64-linux] ,
maybe this is the reason...

David A. Black wrote:

They are indeed the same method:

     rb_define_method(rb_cArray, "", rb_ary_aref, -1);
     rb_define_method(rb_cArray, "slice", rb_ary_aref, -1);

uhm... but if they are the same method, why aliasing the one, the other
does not become aliased? do I have to alias both methods?? and, if yes,
how??? :slight_smile: I've tried, but with no success...

It's one underlying method (C function) with two Ruby names. If you
redefine one of the Ruby names you break the binding between that name
and the underlying method, and if you add an alias then there's a third
name. Each binding is separate; they don't automatically change
together.

David

--
David A. Black, Senior Developer, Cyrus Innovation Inc.

   The Ruby training with Black/Brown/McAnally
   Compleat Philadelphia, PA, October 1-2, 2010
   Rubyist http://www.compleatrubyist.com

David A. Black wrote:

It's one underlying method (C function) with two Ruby names. If you
redefine one of the Ruby names you break the binding between that name
and the underlying method, and if you add an alias then there's a third
name. Each binding is separate; they don't automatically change
together.

uhhh I think I've understood: In order to do what I want to do, I should
implement something like this:

class Array

  def slice_with_regexp(*args)
    return self.select{ |val| val =~ args[0] } if args.size == 1 and
args[0].is_a?(Regexp)
    # code that executes C function called by and slice
  end

  alias_method :slice_with_regexp, :slice
  alias_method :slice_with_regexp, :

end

that is stupid 'cause breaks Array.slice performances.

Right?

Thanks a lot David!

···

--
Posted via http://www.ruby-forum.com/\.

Hi --

David A. Black wrote:

It's one underlying method (C function) with two Ruby names. If you
redefine one of the Ruby names you break the binding between that name
and the underlying method, and if you add an alias then there's a third
name. Each binding is separate; they don't automatically change
together.

uhhh I think I've understood: In order to do what I want to do, I should
implement something like this:

class Array

def slice_with_regexp(*args)
   return self.select{ |val| val =~ args[0] } if args.size == 1 and
args[0].is_a?(Regexp)
   # code that executes C function called by and slice
end

alias_method :slice_with_regexp, :slice
alias_method :slice_with_regexp, :

end

that is stupid 'cause breaks Array.slice performances.

Right?

Thanks a lot David!

I should add that my full advice is to avoid overriding slice and/or
globally. A better way to alter core functionality is on a per-object
basis:

   module ExtendedSlicer
     def slice(*args)
       arg0 = args[0]
       case arg0
       when Regexp
         grep(arg0)
       else
         super
       end
     end
   end

   a = %w{ one two three four }.extend(ExtendedSlicer)
   a.slice(0,2) # ["one", "two"]
   a.slice(/o/) # ["one", "two", "four"]

The main advantage here is that it's a better fit: you need an object to
behave a certain way, and you teach that object to behave that way.
Another advantage is that it makes you work a little harder, and
therefore helps you evaluate more carefully whether or not you need to
perform the modification at all.

David

···

On Sun, 1 Aug 2010, Maurizio De Santis wrote:

--
David A. Black, Senior Developer, Cyrus Innovation Inc.

   The Ruby training with Black/Brown/McAnally
   Compleat Philadelphia, PA, October 1-2, 2010
   Rubyist http://www.compleatrubyist.com

David A. Black wrote:

   module ExtendedSlicer
     def slice(*args)
       arg0 = args[0]
       case arg0
       when Regexp
         grep(arg0)
       else
         super
       end
     end
   end

   a = %w{ one two three four }.extend(ExtendedSlicer)
   a.slice(0,2) # ["one", "two"]
   a.slice(/o/) # ["one", "two", "four"]

The main advantage here is that it's a better fit: you need an object to
behave a certain way, and you teach that object to behave that way.
Another advantage is that it makes you work a little harder, and
therefore helps you evaluate more carefully whether or not you need to
perform the modification at all.

Ok, I've learned more from these posts than in two months of
experiments.

Said that "is a bad idea to override a built-in method", I'm trying to
think in this way now: "what happens if I...", so I tried this:

class MyArray < Array; end

module ExtendedSlicer
  def slice(*args)
    arg0 = args[0]
    case arg0
      when Regexp
        grep(arg0)
      else
        super
    end
  end
end

class MyArray
  include ExtendedSlicer
end

arr = MyArray.new %w{a b c ab}
puts arr.slice(/a/).inspect => it writes ["a", "ab"] ; correct

instead, if I execute:

module ExtendedSlicer
  def slice(*args)
    arg0 = args[0]
    case arg0
      when Regexp
        grep(arg0)
      else
        super
    end
  end
end

class Array
  include ExtendedSlicer
end

arr = Array.new %w{a b c ab}
puts arr.slice(/a/).inspect # => can't convert Regexp into Integer
(TypeError)

Why? Maybe it is forbidden to include a Module in a built-in class, for
security reasons, or... boh! :slight_smile: Can you explain that?

···

--
Posted via http://www.ruby-forum.com/\.

Said that "is a bad idea to override a built-in method", I'm trying to
think in this way now: "what happens if I...", so I tried this:

...

instead, if I execute:

module ExtendedSlicer
def slice(*args)
arg0 = args[0]
case arg0
when Regexp
grep(arg0)
else
super
end
end
end

class Array
include ExtendedSlicer
end

arr = Array.new %w{a b c ab}
puts arr.slice(/a/).inspect # => can't convert Regexp into Integer
(TypeError)

Why? Maybe it is forbidden to include a Module in a built-in class, for
security reasons, or... boh! :slight_smile: Can you explain that?

It's because included modules are inserted after the class in the
method lookup chain. They act more like a superclass in that regard,
methods defined in a module don't override methods defined in classes
which include the module.

consider:

module FooModule
   def foo
     "FooModule brand foo."
   end
end

class FooMaker
   def foo
      "FooMaker brand foo."
   end
end

class FooFighter < FooMaker
   include FooModule
   def foo
      "I'll do my own foo thank you."
   end
end

class FooInheritor < FooMaker
end

class FooIncluder
  include FooModule
end

class FooBothInheritAndInclude < FooMaker
  include FooModule
end

FooMaker.new.foo # => "FooMaker brand foo."
FooFighter.new.foo # => "I'll do my own foo thank you."
FooInheritor.new.foo # => "FooMaker brand foo."
FooBothInheritAndInclude.new.foo # => "FooModule brand foo."

# The ancestors method shows the order in which classes and modules
are searched for a method:

FooMaker.ancestors # => [FooMaker, Object, Kernel]
FooFighter.ancestors # => [FooFighter, FooModule,
FooMaker, Object, Kernel]
FooInheritor.ancestors # => [FooInheritor, FooMaker,
Object, Kernel]
FooBothInheritAndInclude.ancestors # => [FooBothInheritAndInclude,
FooModule, FooMaker, Object, Kernel]

# Note that in the last case the leaf class doesn't define foo, and
the method in FooModule overrides the one in FooMaker

# And of course singleton methods trump everything:

monty = FooBothInheritAndInclude.new
monty.foo # => "FooModule brand foo."

def monty.foo
   "and now for something completely different"
end

monty.foo # => "and now for something completely different"

···

On Sun, Aug 1, 2010 at 12:07 PM, Maurizio De Santis <desantis.maurizio@gmail.com> wrote:

--
Rick DeNatale

Blog: http://talklikeaduck.denhaven2.com/
Github: rubyredrick (Rick DeNatale) · GitHub
Twitter: @RickDeNatale
WWR: http://www.workingwithrails.com/person/9021-rick-denatale
LinkedIn: http://www.linkedin.com/in/rickdenatale

Hello,

Why? Maybe it is forbidden to include a Module in a built-in class, for
security reasons, or... boh! :slight_smile: Can you explain that?

I think it has to do with inheritance when looking for methods.

First, ruby look in the class itself to see if there is a method named
'slice'. When you try to extend Array, it find Array's original
'slice' and so does not need to look at the module (and never find
your redefinition).

If no method of that name is found in the class (like MyArray), it
looks in the modules (if any) included in your class (and find one
'slice' in ExtendedSlicer) before looking at the ancestor class (which
is Array).

If I'm not mistaken, it should explain the observed behavior.

All that (and more ! :o) is explained in David's book (The
Well-Grounded Rubyist) in a crystal-clear manner around p100 of my
version.

Cheers,

···

--
JJ Fleck
PCSI1 Lycée Kléber

Clear as spring wat - better do not mention Spring! - bright as a
flawless Ruby! :smiley: Thanks to everyone! And forgive me for the joke :stuck_out_tongue:

···

--
Posted via http://www.ruby-forum.com/.