A ruby course

Hello Group,

I need more help.

Gavin Sinclair wrote:

p51. Good exercise. How about another?

Anybody has an idea for an exercise on extending existing classes. So
far the only thing I have come up with is:

\exercise{Fibonacci II}{
   Extend \codetype{Integer} with a function fib that calculates the
   corresponding fibonacci number.}

And my creativity is at an all time low.

There are plenty of examples at http://extensions.rubyforge.org. I
was thinking of something like String#indent as an exercise, but you
haven't taught regexen at this point, so that's out. But something
else might do.

p52. It would be good to teach extending objects, because it's core to
     Ruby, and important when understanding "class methods". But you
     can emphasise that it's advanced material, so students needn't be
     worried if they don't get it straight away. (It's not *that*
     hard to accept, anyway... :slight_smile:

Anybody nows of a good example where it is neccessary to extend an
existing object?

I have never used this feature and I don't know when to use it in a
sensible way.

It could be something academic like changing the to_s output for a
particular array/string. But really, the main point of this feature
(adding methods to objects) is to add methods to classes (which are
objects). So perhaps use that as an example.

p55. Missing exercises.

Somebody has an idea for an exercise on using modules (as namespace and
mixin?)

A favourite of mine is building a new class, then mixing in Enumerable
and Comparable. So you're not building a new module, but reusing two
existing ones.

Gavin

···

On Wednesday, October 6, 2004, 3:24:12 AM, Brian wrote:

Curt Hibbs wrote:

Curt Hibbs wrote:

I noticed a couple of typos:

>> ...

Thanks for taking the time to read it!

Regards,

Brian

···

--
Brian Schröder
http://ruby.brian-schroeder.de/

Henrik Horneber wrote:

- extending REXML::Element with a method that strips all unnecessary whitespaces and all children

make that 'and does that to all children'

sorry :slight_smile:

Henrik Horneber wrote:

Brian Schröder wrote:

Hello Group,

I need more help.

Gavin Sinclair wrote:

p51. Good exercise. How about another?

Anybody has an idea for an exercise on extending existing classes. So far the only thing I have come up with is:

\exercise{Fibonacci II}{
  Extend \codetype{Integer} with a function fib that calculates the
  corresponding fibonacci number.}

And my creativity is at an all time low.

Hi!

what about
- extending REXML::Element with a method that strips all unnecessary whitespaces and all children
- extending String so you can tell it to remove cpp comments /* remove me */

Of course both tasks could be accomplished without extending those classes. I have no idea in which cases you should write helper methods and when you should extend existing classes... does anybody have some 'heuristics' or best practices?

regards,
Henrik

Hello Henrik,

Sadly I don't want to introduce REXML (even though its a great library). But this would cost me too much time.

Extending string with something like remove_cpp_comments seems awkward to me. In any usecase for this I'd rather have a class CppCode that I can ask to remove_comments. (Or even better, to give me the source without comments.)

So my personal heuristic tells me not to extend in this case.

thanks for your comment,

Brian

···

--
Brian Schröder
http://ruby.brian-schroeder.de/

There was an excellent example along these lines in the RubyConf '04 blogs. According to Francis Hwang's blog:

"Also, Rich [Kilmer] described a cool little hack: Adding time-unit methods to Fixnum so you can write code like time_to_restart = 5.hours + 15.minutes. Dude, I'm totally stealing that."

I have to agree with the entry, it's a killer trick.

James Edward Gray II

···

On Oct 5, 2004, at 1:20 PM, Edgardo Hames wrote:

You could extend Integer with a fibonacci method, so that you can
write something like:

puts 0.fibonacci > 0
puts 1.fibonacci > 1
puts 5.fibonacci > 5

Edgardo Hames wrote:

Anybody has an idea for an exercise on extending existing classes. So
far the only thing I have come up with is:

Taken from the Design Patterns book, Decorator pattern:

"Attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending
functionality."

You could extend Integer with a fibonacci method, so that you can
write something like:

puts 0.fibonacci > 0
puts 1.fibonacci > 1
puts 5.fibonacci > 5

Should be 12, shouldn't it?

My 2 cents.
Ed

I'm sorry, but this was exactly the example I gave. It seems we have a similar mindset :wink:

Regards,

Brian

···

On Wed, 6 Oct 2004 02:24:12 +0900, Brian Schröder > <ruby@brian-schroeder.de> wrote:

--
Brian Schröder
http://ruby.brian-schroeder.de/

Leo wrote:

Brian Schröder wrote:

Hello Group,

I need more help.

Anybody has an idea for an exercise on extending existing classes.

The code below introduce a new string concatenation operator '/' which concatenates two strings placing '/' beween them. This is useful
when forming path from directory and filename components.

class String
  def /(rhs)
    self.sub(%r{/+$}, '') + '/' + rhs.sub(%r{^/+}, '')
  end end if

Now you can write the following code:

$dir='/usr/local/bin'
$name='ruby'
$full_path= $dir/$name

Also the '/' operator will make sure that the strings are joined by one and
only one slash. Example:
$dir='/badby/formed/dir///'

$name='///foo'

$dir/$name => '/badby/formed/dir/foo'

Note, that poatterns like '///' were removed.

Hope it helps,
Leo

That is short and cool. I think this could be an example for me!

Thanks,

Brian

···

--
Brian Schröder
http://ruby.brian-schroeder.de/

A question someone asked earlier on ruby-talk might be an easy, but
appropriate extension to go with #indent and others:
-- String#pack:

class String
def pack(arg)
    self.split(//).pack(arg)
  end
end

Good luck!
/wad

···

On Wed, 6 Oct 2004 10:36:43 +0900, Gavin Sinclair <gsinclair@soyabean.com.au> wrote:

On Wednesday, October 6, 2004, 3:24:12 AM, Brian wrote:

> Hello Group,

> I need more help.

> Gavin Sinclair wrote:
>>
>> p51. Good exercise. How about another?
>>

> Anybody has an idea for an exercise on extending existing classes. So
> far the only thing I have come up with is:

> \exercise{Fibonacci II}{
> Extend \codetype{Integer} with a function fib that calculates the
> corresponding fibonacci number.}

> And my creativity is at an all time low.

There are plenty of examples at http://extensions.rubyforge.org. I
was thinking of something like String#indent as an exercise, but you
haven't taught regexen at this point, so that's out. But something
else might do.

SNIP<<<

Hi!

Taking over from Brian (sorry :slight_smile: ) I need to ask a question of my own.

Brian Schröder wrote:

Henrik Horneber wrote:

....

- extending String so you can tell it to remove cpp comments /* remove me */

Of course both tasks could be accomplished without extending those classes. I have no idea in which cases you should write helper methods and when you should extend existing classes... does anybody have some 'heuristics' or best practices?

regards,
Henrik

Hello Henrik,

....

Extending string with something like remove_cpp_comments seems awkward to me. In any usecase for this I'd rather have a class CppCode that I can ask to remove_comments. (Or even better, to give me the source without comments.)

So my personal heuristic tells me not to extend in this case.

Now, the reason I was immediatly reminded of that remove_cpp_comments example is, that I wrote a small script some time ago, which used exactly this case. First, I implemented remove_cpp_comments in its own class (which was not called CppCode, but had the same intend), but out of curiosity (and because in ruby I can, dang it! :slight_smile: ) I moved the remove_cpp_comments method to String. Turns out, the method got shorter and somewhat clearer, at least to me. And I could write something like

source_code = IO.readlines(file).join("\n")
source_code.strip_comments!
# instead of source_code = CppCode.strip_comments( source_code)

.... you get the idea.

Even though the code became clearer on that level, I still have to agree with Brian that it is somehow dirty/awkward on a different level to extend String with such a method.

This, formerly extremly happy, Ruby user is confused.

What are your experiences?

regards,

Henrik

That's right, I meant adding the #fibonacci method to Fixnum.

Ed

···

On Wed, 6 Oct 2004 03:40:03 +0900, James Edward Gray II <james@grayproductions.net> wrote:

On Oct 5, 2004, at 1:20 PM, Edgardo Hames wrote:

> You could extend Integer with a fibonacci method, so that you can
> write something like:

"Also, Rich [Kilmer] described a cool little hack: Adding time-unit
methods to Fixnum so you can write code like time_to_restart = 5.hours
+ 15.minutes. Dude, I'm totally stealing that."

--
" Don't relax! It's only your tension that's holding you together."

Here's something I've used from time to time (not completely fleshed
out, since I've just been implementing what I need each time I use it).
It lets you write things like:

1.0[kilometers]

1.0[kilometers][miles]

1.0[seconds][hours]

1.0[miles/hours]

1.0[kilometers/second][miles/hour]

1.0[kilometers] + 1.0[miles]

...and does all the right stuff for you. The n[kilometers][miles] form,
for example, gives you the distance in miles equal to n kilometers.

-- Markus

P.S. RubyLicence, GPL, your choice, yada yada

require '/usr/local/ruby/1.8/extensions.rb'

···

On Tue, 2004-10-05 at 11:40, James Edward Gray II wrote:

On Oct 5, 2004, at 1:20 PM, Edgardo Hames wrote:

There was an excellent example along these lines in the RubyConf '04
blogs. According to Francis Hwang's blog:

"Also, Rich [Kilmer] described a cool little hack: Adding time-unit
methods to Fixnum so you can write code like time_to_restart = 5.hours
+ 15.minutes. Dude, I'm totally stealing that."

I have to agree with the entry, it's a killer trick.

#
# By Markus (MQR) Roberts, 2003+/-1
#
class A_component_vector < Hash
    #
    # Because the keys are dimensions that themselves contain component
    # vectors, we don't want to deep copy past here.
    #
    def deep_copy
        clone
        end
    end

$known_dimensions = {}
class A_dimension
    attr_accessor :name
    def initialize(name)
        @name = name
        end
    def components
        A_component_vector.new(self=>1.0)
        end
    def *(other)
       
A_derived_dimension.new("#{self.name}*#{other.name}",self.components+other.components)
        end
    def /(other)
       
A_derived_dimension.new("#{self.name}/#{other.name}",self.components-other.components)
        end
    def to_str
        @name
        end
    end

class A_derived_dimension < A_dimension
   attr_reader :components,:hash
   def initialize(name,components)
       super(name)
       @components = components
       #@hash = 0
       #@components.each_pair { |k,v| @hash += v*k.hash }
       @hash = @components.inject(0) { |x,p| x+p[1]*p[0].hash }
       end
   def ==(other)
       self.class == other.class and
         @hash == other.hash and
         @components.length == other.components.length and
         @components.all? { |k,v|
             v == other.components[k]
             }
       end
   end

def define_dimension(name,components=nil)
    name = name.to_s.intern
    $known_dimensions[name] = components ?
A_derived_dimension.new(name,components.components) :
A_dimension.new(name)
    #eval "#{name.to_s.capitalize} = $known_dimensions[#{name.inspect}]"
    eval %Q{
        def #{name}
            $known_dimensions[#{name.inspect}]
            end
        }
    end

define_dimension :duration
define_dimension :distance
define_dimension :area, distance*distance
define_dimension :speed, distance/duration
#define_dimension :velocity, displacement/duration

$known_units = {}
class A_unit
   attr_accessor :what,:how_much,:name
   def initialize
       end
   def A_unit.to_measure(what,name,conversion=nil)
       result = A_unit.new
       result.name = name
       conversion = 1.0 unless conversion
       conversion = conversion.value*conversion.units.how_much if
conversion.is? A_measure
       $known_units[what] ||= {}
       $known_units[what][result] = conversion
       result.what = what
       result.how_much = conversion
       result
       end
   def *(other)
      
A_unit.to_measure(self.what*other.what,"(#{self.name})*(#{other.name})",self.how_much*other.how_much)
       end
   def /(other)
      
A_unit.to_measure(self.what/other.what,"(#{self.name})/(#{other.name})",self.how_much/other.how_much)
       end
   def to(units)
       return 1.0 if units == self
       fail "Can't coerce #{self.what.name} to #{units.what.name}" if
self.what != units.what
       fail "Can't coerce #{self} to #{units}" if self.what !=
units.what
       return self.how_much/units.how_much
       end
   def to_s
       @name #$known_units[@what]
       end
   def to_str
       @name #$known_units[@what]
       end
   end

class A_measure
    attr_reader :value,:units
    def initialize(value,units)
        @value = value
        @units = units
        end
    def (units)
        p "Trying to convert #{self} to #{units}\n"
        A_measure.new(@units.to(units)*@value,units)
        #@units.to(units)*@value
        end
    def *(other)
        case other
          when A_measure then
A_measure.new(@value*other.value,@units*other.units)
          else A_measure.new(@value*other,
@units )
          end
        end
    def /(other)
        case other
          when A_measure then
A_measure.new(@value/other.value,@units/other.units)
          else A_measure.new(@value/other,
@units )
          end
        end
    def +(other)
        A_measure.new(@value+other[@units].value,@units)
        end
    def -(other)
        A_measure.new(@value-other[@units].value,@units)
        end
    def to_s
        "#{value}[#{units}]"
        end
    def to_str
        "#{value}[#{units}]"
        end
    end
    
class Numeric
    def (units)
        A_measure.new(self,units)
        end
    end

def define_unit_of(what,names,comparison=nil)
    names = [names,names.to_s+'s'] unless names.is_a? Array
    names.each { |name|
        eval %Q{
            def #{name}(quantity=nil)
                if quantity
                    A_measure.new(quantity,#{name})
                  else
                   
A_unit.to_measure(#{what.name},"#{name}",#{comparison ? comparison :
'nil'})
                  end
                end
            }
        }
    end
    
def define_metric_unit_of(what,name,comparison=nil)
    define_unit_of what,name,comparison
    define_unit_of what,'nano' +name.to_s,1e-9[name]
    define_unit_of what,'micro'+name.to_s,1e-6[name]
    define_unit_of what,'milli'+name.to_s,1e-3[name]
    define_unit_of what,'centi'+name.to_s,1e-2[name]
    define_unit_of what,'deci' +name.to_s,1e-1[name]
    define_unit_of what,'deka' +name.to_s,1e+1[name]
    define_unit_of what,'kilo' +name.to_s,1e+3[name]
    define_unit_of what,'mega' +name.to_s,1e+6[name]
    define_unit_of what,'giga' +name.to_s,1e+9[name]
    end

define_metric_unit_of distance, :meter
define_unit_of distance, [:foot,:feet], 0.3048[meters]
define_unit_of distance, :mile, 1609.344[meters]
define_metric_unit_of duration, :second
define_unit_of duration, :minute, 60.0[seconds]
define_unit_of duration, :hour, 60.0[minutes]

fail "Type checking doesn't work!" unless begin
    print "#{1.0[kilometers] + 1.0[seconds]}\n"
    false
  rescue
    true
  end

print "#{1.0[kilometers]}\n"
print "#{1.0[kilometers][miles]}\n"
print "#{1.0[seconds][hours]}\n"
print "#{1.0[miles/hours]}\n"
print "#{1.0[kilometers/second][miles/hour]}\n"
print "#{1.0[kilometers] + 1.0[miles]}\n"

Edgardo Hames wrote:
>
>>Anybody has an idea for an exercise on extending existing classes. So
>>far the only thing I have come up with is:
>>
>
>
> Taken from the Design Patterns book, Decorator pattern:
>
> "Attach additional responsibilities to an object dynamically.
> Decorators provide a flexible alternative to subclassing for extending
> functionality."
>
> You could extend Integer with a fibonacci method, so that you can
> write something like:
>
> puts 0.fibonacci > 0
> puts 1.fibonacci > 1
> puts 5.fibonacci > 5

Should be 12, shouldn't it?

0.fib > 0
1.fib > 1
2.fib > 1
3.fib > 2
4.fib > 3
5.fib > 5

I'm sorry, but this was exactly the example I gave. It seems we have a
similar mindset :wink:

I'm really sorry, I must admit I didn't really paid much attention to
the example. I'm replying messages while in a training session... :frowning:

But anyway, you can think of the Visitor or Decorator pattern, for
extending classes. Did I mention adding a fib method to Fixnum? :wink:

Regards,
Ed

···

On Wed, 6 Oct 2004 04:18:17 +0900, Brian Schröder <ruby@brian-schroeder.de> wrote:

> On Wed, 6 Oct 2004 02:24:12 +0900, Brian Schröder > > <ruby@brian-schroeder.de> wrote:

--
" Don't relax! It's only your tension that's holding you together."

Maybe extend this, then:

  module InlineMath
    def fib; ...; end
    def fac; ...; end
    def sin; ...; end
    def cos; ...; end
    def tan; ...; end
  end

  class Numeric
    include InlineMath
  end

Students have to implement those methods, and try it out with real
numbers. Caveat: what about 3.5.fib and 3.5.fac? Do the
trigonometric methods assume degress or radians?

Gavin

···

On Wednesday, October 6, 2004, 5:18:17 AM, Brian wrote:

Edgardo Hames wrote:

On Wed, 6 Oct 2004 02:24:12 +0900, Brian Schröder >> <ruby@brian-schroeder.de> wrote:

Anybody has an idea for an exercise on extending existing classes. So
far the only thing I have come up with is:

Taken from the Design Patterns book, Decorator pattern:

"Attach additional responsibilities to an object dynamically.
Decorators provide a flexible alternative to subclassing for extending
functionality."

You could extend Integer with a fibonacci method, so that you can
write something like:

puts 0.fibonacci > 0
puts 1.fibonacci > 1
puts 5.fibonacci > 5

Should be 12, shouldn't it?

My 2 cents.
Ed

I'm sorry, but this was exactly the example I gave. It seems we have a
similar mindset :wink:

I usually try not to do that kind of things. Then, for each new
lanaguage you are going to support, you should now add a
remove_#{lang}_comments to String. But then, when reading a given
source file you could remove comments that are not so in that
language.

Regards,
Ed

···

On Wed, 6 Oct 2004 03:29:35 +0900, Henrik Horneber <ryco@gmx.net> wrote:

>> - extending String so you can tell it to remove cpp comments /* remove
>> me */
>
> Extending string with something like remove_cpp_comments seems awkward
> to me. In any usecase for this I'd rather have a class CppCode that I
> can ask to remove_comments. (Or even better, to give me the source
> without comments.)
>
> So my personal heuristic tells me not to extend in this case.

Now, the reason I was immediatly reminded of that remove_cpp_comments
example is, that I wrote a small script some time ago, which used
exactly this case. First, I implemented remove_cpp_comments in its own
class (which was not called CppCode, but had the same intend), but out
of curiosity (and because in ruby I can, dang it! :slight_smile: ) I moved the
remove_cpp_comments method to String. Turns out, the method got shorter
and somewhat clearer, at least to me. And I could write something like

source_code = IO.readlines(file).join("\n")
source_code.strip_comments!
# instead of source_code = CppCode.strip_comments( source_code)

.... you get the idea.

Even though the code became clearer on that level, I still have to agree
with Brian that it is somehow dirty/awkward on a different level to
extend String with such a method.

--
" Don't relax! It's only your tension that's holding you together."

Edgardo Hames wrote:

You could extend Integer with a fibonacci method, so that you can
write something like:

puts 0.fibonacci > 0
puts 1.fibonacci > 1
puts 5.fibonacci > 5

Should be 12, shouldn't it?

0.fib > 0
1.fib > 1
2.fib > 1
3.fib > 2
4.fib > 3
5.fib > 5

Oh, 12 was the sum of the fibonaccy numbers up to 5. Too much thinking is bad for your brain.

···

--
Brian Schröder
http://ruby.brian-schroeder.de/

Edgardo Hames wrote:

- extending String so you can tell it to remove cpp comments /* remove
me */

Extending string with something like remove_cpp_comments seems awkward
to me. In any usecase for this I'd rather have a class CppCode that I
can ask to remove_comments. (Or even better, to give me the source
without comments.)

So my personal heuristic tells me not to extend in this case.

Now, the reason I was immediatly reminded of that remove_cpp_comments
example is, that I wrote a small script some time ago, which used
exactly this case. First, I implemented remove_cpp_comments in its own
class (which was not called CppCode, but had the same intend), but out
of curiosity (and because in ruby I can, dang it! :slight_smile: ) I moved the
remove_cpp_comments method to String. Turns out, the method got shorter
and somewhat clearer, at least to me. And I could write something like

source_code = IO.readlines(file).join("\n")
source_code.strip_comments!
# instead of source_code = CppCode.strip_comments( source_code)

.... you get the idea.

Even though the code became clearer on that level, I still have to agree
with Brian that it is somehow dirty/awkward on a different level to
extend String with such a method.

I usually try not to do that kind of things. Then, for each new
lanaguage you are going to support, you should now add a
remove_#{lang}_comments to String. But then, when reading a given
source file you could remove comments that are not so in that
language.

Regards,
Ed

In my case there is never going to be a different language to support, but in general, that is a good reason not to do it. I'd like to get away existing classes. I could have written my own subclass of String called CppSource, so I could have written

source_code = CppSource.new( IO.readlines(file).join("\n") )
source_code.strip_comments!

which might actually be a better solution than to put strip_comments! into String. On the other hand (again stealing from the other thread), writing

time_to_restart = TimeNumeric.new(5).hours + TimeNumeric.new(15).minutes

is just what we would like to avoid. So, as a guideline, should you only extend an existing class when you use lots of literals in your code?

regards,

Henrik

···

On Wed, 6 Oct 2004 03:29:35 +0900, Henrik Horneber <ryco@gmx.net> wrote:

from my example and to a little wider discussion of when to extend

What are you talking about? As far as I know the fibonacci series is defined as

fib n = fib (n-1) + fib (n-2)

Then,
fib 5 =
= fib 4 + fib 3
= (fib 3 + fib 2) + (fib 2 + fib 1)
= ((fib 2 + fib 1) + (fib 1 + fib 0)) + (fib 1 + fib 0) + (1+0)
= ((fib 1 + fib 0) + (1+ 0))+ (1+0) + (1+0) + (1+ 0)
= 1+1+1+1+1
= 5

You might cosider taking a look at

It defines fib 0 = 0, but either way, 12 doesn't belong to the series.

Regards,
Ed

···

On Wed, 6 Oct 2004 04:36:23 +0900, Brian Schröder <ruby@brian-schroeder.de> wrote:

Edgardo Hames wrote:
>>>
>>>You could extend Integer with a fibonacci method, so that you can
>>>write something like:
>>>
>>>puts 0.fibonacci > 0
>>>puts 1.fibonacci > 1
>>>puts 5.fibonacci > 5
>>
>>Should be 12, shouldn't it?
>
>
> 0.fib > 0
> 1.fib > 1
> 2.fib > 1
> 3.fib > 2
> 4.fib > 3
> 5.fib > 5
>

Oh, 12 was the sum of the fibonaccy numbers up to 5. Too much thinking
is bad for your brain.

--
" Don't relax! It's only your tension that's holding you together."

Edgardo Hames wrote:

···

On Wed, 6 Oct 2004 04:36:23 +0900, Brian Schröder > <ruby@brian-schroeder.de> wrote:

Edgardo Hames wrote:

You could extend Integer with a fibonacci method, so that you can
write something like:

puts 0.fibonacci > 0
puts 1.fibonacci > 1
puts 5.fibonacci > 5

Should be 12, shouldn't it?

0.fib > 0
1.fib > 1
2.fib > 1
3.fib > 2
4.fib > 3
5.fib > 5

Oh, 12 was the sum of the fibonaccy numbers up to 5. Too much thinking
is bad for your brain.

What are you talking about? As far as I know the fibonacci series is defined as

fib n = fib (n-1) + fib (n-2)

Then,
fib 5 = = fib 4 + fib 3
= (fib 3 + fib 2) + (fib 2 + fib 1)
= ((fib 2 + fib 1) + (fib 1 + fib 0)) + (fib 1 + fib 0) + (1+0)
= ((fib 1 + fib 0) + (1+ 0))+ (1+0) + (1+0) + (1+ 0)
= 1+1+1+1+1
= 5

You might cosider taking a look at Fibonacci Number -- from Wolfram MathWorld
It defines fib 0 = 0, but either way, 12 doesn't belong to the series.

Regards,
Ed

I meant "bad for _my_ brain". Sorry if I have confused you. You were absolutely right, and I calculated the wrong thing.

Regards,

Brian

--
Brian Schröder
http://ruby.brian-schroeder.de/

imho, there are two occasions for extending a class:
1. When the added methods are useful for that class in general (ie,
String#rot13, Integer#factors)
2. When you're just hacking :slight_smile:

If the data that you are working with is sort of an extended version
of the class, that's when I would subclass. I think the common text
markup modules are excellent examples of this; redcloth and bluecloth
both act just like strings, but the have a few important methods that
treat them as specific data types.

In this vein, TimeNumeric might be subclassed to use like this:
# TimeNumeric < Float
... where the float value is the number of seconds.

time_to_shutdown = TimeNumeric.new(5*60*60 + 15*60)
or better yet:
time_to_shutdown = TimeNumeric.hours(5.25)
or even:
time_to_shutdown = TimeNumeric["5:15:00"] # calls "#to_time_numeric"
on the string

cheers,
Mark

···

On Wed, 6 Oct 2004 03:55:12 +0900, Henrik Horneber <ryco@gmx.net> wrote:

Edgardo Hames wrote:

> On Wed, 6 Oct 2004 03:29:35 +0900, Henrik Horneber <ryco@gmx.net> wrote:
>
>>>>- extending String so you can tell it to remove cpp comments /* remove
>>>>me */
>>>
>>>Extending string with something like remove_cpp_comments seems awkward
>>>to me. In any usecase for this I'd rather have a class CppCode that I
>>>can ask to remove_comments. (Or even better, to give me the source
>>>without comments.)
>>>
>>>So my personal heuristic tells me not to extend in this case.
>>
>>Now, the reason I was immediatly reminded of that remove_cpp_comments
>>example is, that I wrote a small script some time ago, which used
>>exactly this case. First, I implemented remove_cpp_comments in its own
>>class (which was not called CppCode, but had the same intend), but out
>>of curiosity (and because in ruby I can, dang it! :slight_smile: ) I moved the
>>remove_cpp_comments method to String. Turns out, the method got shorter
>> and somewhat clearer, at least to me. And I could write something like
>>
>>source_code = IO.readlines(file).join("\n")
>>source_code.strip_comments!
>># instead of source_code = CppCode.strip_comments( source_code)
>>
>>.... you get the idea.
>>
>>Even though the code became clearer on that level, I still have to agree
>>with Brian that it is somehow dirty/awkward on a different level to
>>extend String with such a method.
>>
>
>
> I usually try not to do that kind of things. Then, for each new
> lanaguage you are going to support, you should now add a
> remove_#{lang}_comments to String. But then, when reading a given
> source file you could remove comments that are not so in that
> language.
>
> Regards,
> Ed

In my case there is never going to be a different language to support,
but in general, that is a good reason not to do it. I'd like to get away
from my example and to a little wider discussion of when to extend
existing classes. I could have written my own subclass of String called
CppSource, so I could have written

source_code = CppSource.new( IO.readlines(file).join("\n") )
source_code.strip_comments!

which might actually be a better solution than to put strip_comments!
into String. On the other hand (again stealing from the other thread),
writing

time_to_restart = TimeNumeric.new(5).hours + TimeNumeric.new(15).minutes

is just what we would like to avoid. So, as a guideline, should you only
extend an existing class when you use lots of literals in your code?

But this is all fairly subjective, isn't it? Among some programmers, String#rot13 might be a really sensible thing to add, but to others maybe it might seem like cruft. One interesting example from RubyConf was Rich Kilmer saying that he added methods like #minutes and #days to Fixnum, so he could say things like

15.minutes + 2.hours

It's also very easy to imagine using this for, say, feet and inches:

1.miles + 6.feet + 6.inches

... which is culturally specific, isn't it? I mean, you're not going to use that if you use the metric system like a civilized people.

There are two competing philosophies in OO theory. On one hand, you have the Platonists who believe that types are eternal forms that can be found in nature, so if you did your analysis right you'd find those forms and be able to codify them in your class definitions. On the other hand, you have the Pragmatists who believe that types are just shorthand, to be defined and discarded as the situation allows.

Ruby is more of a Pragmatist language, which personally I like 'cause on some deep level it jibes with my own belief in the subjectivity of experience. So, you know, if you want to extend a class, do it if it makes your life easier. If it doesn't, then don't do it.

Francis Hwang

AIM: francisrhizome

···

On Oct 5, 2004, at 5:22 PM, Mark Hubbart wrote:

imho, there are two occasions for extending a class:
1. When the added methods are useful for that class in general (ie,
String#rot13, Integer#factors)
2. When you're just hacking :slight_smile: