Module philosophy

Sorry to beat a dead horse, but to confirm: the only way to mix a
module into an already
existing class it to reopen it with something (ugly) like this:

class String; include Crc; end

(1) Is that right?

And then to mix a module into an instantiated object, you can use
extend, and that
modifies the one object. (2) Philosophically, is there any reason why
extend can’t be used
in the same way on classes? (ie. String.extend(Crc))

One other question (3), below is an example of my SelfCrc module. This
module
works well when included into String, but extend doesn’t work because
that
only modifies the one object and .crc16_ok? below tries to call
.get_crc16
on a new string that it creates, which fails.

I can modify it to work like so:

    def crc16_ok?
first_part = self[0..-3]
first_part.extend(SelfCrc)
first_part.get_crc16 == get_terminating_crc16

end

…is that the right way to make this module work properly? Or are
some modules just not suitable for extend?

module SelfCrc

Returns the CCITT CRC16 for the current object

def get_crc16
@@crctab ||= CrcTab.new
@@crctab.get(self)
end

Converts the last two characters of this string to

a numeric CRC and returns it

def get_terminating_crc16
(self[-2, 1][0] << 8) + (self[-1, 1][0])
end

Calculates a CRC for the string, converts it into two

characters and adds them to the end of the string

def add_terminating_crc16!
replace(self + get_crc16.to_s16)
end

Removes the last two characters from the string

def remove_terminating_crc16!
replace(self[0…-3])
end

Extracts two characters from the end of the string, converts

them to a numeric CRC and compares that CRC to the CRC of

the rest of the string. Returns true if the CRC is correct.

def crc16_ok?
self[0…-3].get_crc16 == get_terminating_crc16
end
end

Thanks!

Leslie

On Wednesday 04 June 2008, Leslie V. wrote:

modifies the one object. (2) Philosophically, is there any reason why
extend can’t be used
in the same way on classes? (ie. String.extend(Crc))

Because String.extend(Crc) would add the instance methods of the Crc
module as
class methods of String, not as instance methods. This would mean you
could
do:

String.method_defined_in_crc

but not

“hello”.method_defined_in_crc

If you don’t like the idea of reopening the class, you can use include
with
send:

String.send :include, Crc

Stefano

On 4 Jun., 15:49, Leslie V. [email protected] wrote:

modifies the one object. (2) Philosophically, is there any reason why
extend can’t be used
in the same way on classes? (ie. String.extend(Crc))

You need to be able to distinguish between importing class methods and
importing instance methods (see Stefano’s reply).

            first_part.get_crc16 == get_terminating_crc16
    end

…is that the right way to make this module work properly? Or are
some modules just not suitable for extend?

If your module expects to be present in all instances of the class
then including is the only reasonable way to do it IMHO.

I can think of a number of other designs which would not have this
issue but since I do not exactly know what your module does
(especially CrcTab which could be a kind of cache) I am reluctant to
enumerate them.

Assuming that you want to store strings along with their CRC’s I’d
rather have a separate class that has a String and a CRC. That seems
more natural to me. This would also avoid the issue that you cannot
easily determine whether a String does contain its CRC or not etc.

Kind regards

robert

On Wednesday 04 June 2008 08:49:48 Leslie V. wrote:

Sorry to beat a dead horse, but to confirm: the only way to mix a
module into an already
existing class it to reopen it with something (ugly) like this:

class String; include Crc; end

(1) Is that right?

Short answer: You’ll have to mess with it dynamically, but that is far
from
the only syntax to do so. Other obvious ones:

String.class_eval { include Crc }
String.send :include, Crc

And there are always stranger things like:

“foo”.class.class_eval { include Crc }

And then to mix a module into an instantiated object, you can use
extend, and that
modifies the one object.

Yes, but keep in mind: "include"ing a module into a class will affect
all
existing and future instances of that class, and of any subclasses. So
you’re
probably not going to extend if you include.

(2) Philosophically, is there any reason why
extend can’t be used
in the same way on classes? (ie. String.extend(Crc))

As others have said, they do different things – include adds instance
methods
to a class, and extend adds methods to an object.

It’s a tricky concept, but remember that classes are objects, too. So:

some_string.extend(Crc) # will define some_string.crc16_ok?
String.extend(Crc) # will define String.crc16_ok?

So, extend works on the actual object you use it on, and include works
on any
instances of it. You could do this, for example:

Class.send :include, Crc

Now all classes will have a crc16_ok? class method, for example:

String.crc16_ok?

That’s probably not what you want.

…is that the right way to make this module work properly? Or are
some modules just not suitable for extend?

I think that comes down to personal philosophy – I haven’t seen (or
used) ‘extend’ a whole lot. It seems most useful when you’d only want to
apply it to a single object, and then, I’d usually use instance_eval
instead.

On the other hand, including it into String means you’re affecting all
Strings, and polluting String’s method namespace. It means that no one
else
can define String#get_crc16.

The other way would be to use a separate class – either something which
contains both a String and a CRC (as Robert suggested), or something
which
inherits from String.

And either way, depending on how much work you want to do, you could
always
override all methods on String which return a String, and force them to
either return an extended String, or return a CrcString, depending on
how you
decide to do it.

It seems like a good plan to me, but the lack of a
public String.include() method makes me think this is a
very rare plan.

It might be rare because in reality this is not so much a
problem IMHO, despite some people claiming it could be. (I had
an angry IRC “discussion” with a C# proponent over this, I
really dislike people iterating about the “danger” of concepts
despite others applying them for many years without any
problem … )

What could be nice is to store different “states” of ruby classes
and restore to them at user’s will. (Or additionally restore the
“start” the state of slowly “evolved” ruby classes as well).

I mean both standard classes (more important) and user-built
ones (less important, IMHO). It is interesting to see that
someone like the sandbox of _why was more an afterthought
than an initial design idea. I think there is plenty of
more room in this direction, who knows… maybe in a few
years. Ruby is like the OOP language that goes towards
new paradigms - with class derived objects - the most easiest.

Ruby is great but it is not perfect for everyone or every
use case. And since 99% of new languages will have crappy
syntax, the most likely language to beat Ruby will be …

Ruby! :slight_smile:

Holy macaroni, thanks for these replies!

What I am trying to accomplish here is the optional modification of
core classes. So my CRC lib can handle useful calculations, and
optionally add convenient methods to String and Fixnum too.

That way people can have the convenience if they like, but they must
explicitly authorise it with String.send(:include …). It seems like a
good plan to me, but the lack of a public String.include() method
makes me think this is a very rare plan.

These strings with CRC’s on the ends are coming in from sockets, and I
have to check them, extract them, use them and then put CRC’s on the
end of the replies before sending them out. In this case it’s quite
nice to do:

begin
msg, sender = @socket.recvfrom(1500)
end while (!msg.crc16_ok?)
msg.remove_terminating_crc16!

  • but it’s true that I might forget to remove that crc. So perhaps
    decend from String like you say:

begin
msg, sender = @socket.recvfrom(1500)
msg = CrcString(msg)
end while (!msg.crc16_ok?)
(now use msg directly)

  • and I’d know immediately if I’d forgotten to construct the CrcString
    because crc16_ok? would fail.

And then a third option would be to modify Socket:
class Socket
def recv_checked(n)
CrcString(recvfrom(n))
end
end

…which I’d again attempt by providing a module that I’d
Socket.send(:include, CrcSocket)

Or once again I could override both String and Socket to make
CrcString and CrcSocket which returns CrcStrings. You’re never short
on options with Ruby!

Les

On Wednesday 04 June 2008 13:53:26 Leslie V. wrote:

Holy macaroni, thanks for these replies!

What I am trying to accomplish here is the optional modification of
core classes. So my CRC lib can handle useful calculations, and
optionally add convenient methods to String and Fixnum too.

Probably included, then, rather than extended.

That way people can have the convenience if they like, but they must
explicitly authorise it with String.send(:include …). It seems like a
good plan to me, but the lack of a public String.include() method
makes me think this is a very rare plan.

Generally, people don’t expect to have to do more than a require to have
the
library ready to use. So I’d do that yourself, somewhere in the library
file.

Perhaps people don’t have this problem very often, but I once spent
three days puzzling over a Rails app that worked fine on my computer,
but not at the web host. A library had been installed by the host that
created a class that was require’d somewhere down the line. That class
had the same name as one of my models, and it totally killed my app.

Having a dynamic base environment works fine until there’s a clash. As
the code base grows, so do the chances of a clash and also the
difficulty in finding it.

On Wed, Jun 4, 2008 at 3:20 PM, Leslie V. [email protected]
wrote:

Perhaps people don’t have this problem very often, but I once spent
three days puzzling over a Rails app that worked fine on my computer,
but not at the web host. A library had been installed by the host that
created a class that was require’d somewhere down the line. That class
had the same name as one of my models, and it totally killed my app.

Having a dynamic base environment works fine until there’s a clash. As
the code base grows, so do the chances of a clash and also the
difficulty in finding it.

I had a similar experience a while back wherein I inadvertently
overrode a method defined by ActiveRecord somewhere. At the time, I
wondered if it might be a good idea to have the ruby interpreter warn
when a method was overridden, unless either the original method was
flagged as silently over-ridable (e…g Object.to_s) or the overriding
method was flagged as intentional (e.g. overriding
ActiveRecord::Base.find). It’s almost certainly not worth the hassle
even if the approach is sound, but after spending some hours digging
through the darker corners of the ActiveRecord code, it seemed like an
awfully attractive notion.

  • donald

ruby -w does warn on method redefinitions!

On Wed, Jun 4, 2008 at 3:48 PM, Leslie V. [email protected]
wrote:

ruby -w does warn on method redefinitions!

True, and I ought to have thought to try that at the time, but unless
I missed something, there’s no way to flag if your overrides are
intentional or not.

  • donald

On Wednesday 04 June 2008 15:20:16 Leslie V. wrote:

Perhaps people don’t have this problem very often, but I once spent
three days puzzling over a Rails app that worked fine on my computer,
but not at the web host. A library had been installed by the host that
created a class that was require’d somewhere down the line. That class
had the same name as one of my models, and it totally killed my app.

How was it require’d, though?

That’s the thing – yes, there will be a clash, but it’ll very likely be
found
as soon as you require both of them.