[ANN] Ruby/Interface

S

Sean O'Dell

Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/


=== PURPOSE

Ruby/Interface was designed to help developers understand the purpose of Ruby
objects encountered at run-time. Ruby is very dynamic, and it is sometimes
hard to determine what function any given object serves, and the methods to
which it responds to fulfill that function.

Ruby/Interface tries to solve this problem by “tagging†objects as
implementing an interface. The implementation is enforced by comparing an
object’s methods against the methods expected by the interface. When an
object is created from a class which claims to implement an interface, or
which includes a module that does, the object is flagged as properly
implementing that interface so long as its methods take the same number of
parameters as the methods described by the interface.

Sean O'Dell
 
C

Curt Hibbs

Sean said:
Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/


=== PURPOSE

Ruby/Interface was designed to help developers understand the
purpose of Ruby
objects encountered at run-time. Ruby is very dynamic, and it is
sometimes
hard to determine what function any given object serves, and the
methods to
which it responds to fulfill that function.

Ruby/Interface tries to solve this problem by “tagging†objects as
implementing an interface. The implementation is enforced by comparing an
object’s methods against the methods expected by the interface. When an
object is created from a class which claims to implement an interface, or
which includes a module that does, the object is flagged as properly
implementing that interface so long as its methods take the same
number of
parameters as the methods described by the interface.

Very nice... I like it!

Curt
 
M

Mauricio Fernández

Ruby/Interface tries to solve this problem by ???tagging??? objects as
implementing an interface. The implementation is enforced by comparing an
object???s methods against the methods expected by the interface.

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com

<miguel> any new sendmail hole I have to fix before going on vacations?
-- Seen on #Linux
 
P

Peter C. Verhage

Sean said:
Ruby/Interface 0.1-1

Very nice!

But wouldn't implements_interface(...) instead of declare_interface(...)
be better, e.g "implements" is the Java terminology for implementing an
interface? And maybe also implements_interface?(...) instead of
has_interface?(...). Of aliasses for both.

Regards,

Peter
 
S

Sean O'Dell

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

I have to ponder this a bit. I admit I am not entirely clear how Ruby works
internally, and singletons are something I don't quite get. Let me digest
the suggestions.

Sean O'Dell
 
S

Sean O'Dell

Very nice!

But wouldn't implements_interface(...) instead of declare_interface(...)
be better, e.g "implements" is the Java terminology for implementing an
interface? And maybe also implements_interface?(...) instead of
has_interface?(...). Of aliasses for both.

Probably right. I'll change it. Seems I'm going to be fiddling it with it
some more today anyway, may as well take of that now!

Sean O'Dell
 
S

Sean O'Dell

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.

Per-class tagging is what I intended. There are three tag states: not tagged,
tagged but unverified, and tagged and verified. Tagging initially only means
"objects of this class may implement this interface." A check of the object
itself later will determine if the object really does implement the
interface.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

I see what you mean. I need to move the storage of the checks themselves to
the object, absolutely correct. I think what I was thinking when I did it
this way was, since I get no "module_added" messages when methods are added
to objects with <<, there was no sense in storing the information with the
objects themselves. But I see that there's an inconsistency that way. I'll
change that.
Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

No, not necessarily. Classes derived from other classes should inherit the
interface. The initial tag only means "may implement the interface" not
"does implement it." The final test is a comparison of the methods of the
individual object methods and their arity.
I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

Ah, I see...thanks for pointing me at this. I was never really aware of klass
before. I just read about how it relates to Singleton's, and it gave a whole
heap of ideas on how I could do this better.

Sean O'Dell

Sean O'Dell
 
S

Sean O'Dell

Some comments:

I see you're using per-class tagging, hence being unable to track
changes in the singleton class. I'd point you again to the last sections
of http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/102555
which I believe you didn't read.
There's an inconsistency in the code: you're checking the methods of the
object (including its singleton methods) with Object#method but storing
the result in a per-class structure; that won't fly.

Also, using class variables instead of class instance variables ties the
notion of interface to inheritance, which I believe you wanted to avoid.

I would consider storing the conformance information in
ROBJECT(self)->klass directly; this will be either the real class or
the singleton class. In the first case, the "conformity table" is
shared by all objects of that class; in the second, it will be "owned"
by the object. The advantage is that this new table would be created
on demand, only when the object is extended/a singleton method is
defined and has_interface? is called. You wouldn't even need to capture
singleton_method_added this way.

Moving things around to work out of klass helped a lot, Mauricio, thanks for
the advice. Got a new version up and running already. It handles Singletons
nicely now.

Sean O'Dell
 
S

Sean O'Dell

Ruby/Interface 0.1-1

Homepage/Documentation: http://interface.rubyforge.org/

Nice. But the Hash example on the page should be more comprehensive.
It's ironic that this was driven by wanting to tell an array from a
hash, and you've defined a :hash interface with just [] and []= !

The name :hash tells you the purpose of the interface, not the method list.
The method list is just to aid enforcement (to ensure they exist for the
interface, and to ensure they have the same parameters so you can always
depend on them being there as you expect them).

[].has_interface?:)hash) => false

Make sense?

Sean O'Dell
 
D

David A. Black

Hi --

Changed the name of the library to celsoft.com/interface.

Ignore previous message (similar idea in spirit though
flipped the other way) -- hadn't seen this one.


David
 
S

Sean O'Dell

I decided the library wasn't "Ruby" enough and I got some ideas from Daniel
Berger's interface library, so I made a lot of changes.

Homepage: http://interface.rubyforge.org/
Rubyforge Project Homepage: http://rubyforge.org/projects/interface/
Download: http://rubyforge.org/frs/?group_id=266&release_id=529

Code Snippet:

require "celsoft.com/interface"

HashInterface = Interface.new

HashInterface.add_class_patterns(Hash, :[], :[]=)
HashInterface.add_pattern:)each_pair, proc{||})

class Hash
implements HashInterface
end

class<<ENV
implements HashInterface
end

p Hash.new.implements?(HashInterface) == true
p ENV.implements?(HashInterface) == true

HashInterface.add_pattern:)each_pair, proc{|a|}) # method param changed

p ENV.implements?(HashInterface) == false

How It Works

Method patterns are stored with each Interface object and are used to check
against the methods of any given object. The name and number of parameters
are important. If an object never claims to implement an interface, it will
always report that it does not implement a given interface, even if it may be
capable of it. When an object claims to implement an interface, a thorough
check is performed to verify that it actually does. This check is only
performed when necessary for efficiency, when implements? is called. The
result of the check is cached and returned quickly on subsequent calls to
implements?. If either the interface definition changes, or methods are
changed in the class of the object, the cache is dumped and the thorough
check is performed the next time implements? is called, and the answer is
then again cached.
 
M

Mauricio Fernández

If either the interface definition changes, or methods are
changed in the class of the object, the cache is dumped and the thorough
check is performed the next time implements? is called, and the answer is
then again cached.

You might want to capture Module#remove_method and
Module#undef_method...

--
Running Debian GNU/Linux Sid (unstable)
batsman dot geo at yahoo dot com
Linux is not user-friendly.
It _is_ user-friendly. It is not ignorant-friendly and idiot-friendly.
-- Seen somewhere on the net
 
J

Jean-Hugues ROBERT

I decided the library wasn't "Ruby" enough and I got some ideas from Daniel
Berger's interface library, so I made a lot of changes.

Homepage: http://interface.rubyforge.org/
Rubyforge Project Homepage: http://rubyforge.org/projects/interface/
Download: http://rubyforge.org/frs/?group_id=266&release_id=529

Code Snippet:

require "celsoft.com/interface"

HashInterface = Interface.new

HashInterface.add_class_patterns(Hash, :[], :[]=)
HashInterface.add_pattern:)each_pair, proc{||})

class Hash
implements HashInterface
end

class<<ENV
implements HashInterface
end

p Hash.new.implements?(HashInterface) == true
p ENV.implements?(HashInterface) == true

Should be implement?() vs implements?() I believe.
See include?() for example.
That is a nice Ruby idiom.

Also: You are using both "declarative" (like "implements")
and "imperative" styles (like "add_pattern). I feel like
"declarative" style would be appropriate in both cases.

OTOH that might be a matter of style/taste.

Yours,

JeanHuguesRobert
EOM
 
S

Sean O'Dell

At 04:55 10/06/2004 +0900, you wrote:

Should be implement?() vs implements?() I believe.
See include?() for example.
That is a nice Ruby idiom.

It's not proper English, but perhaps implement? is more "Ruby."
Also: You are using both "declarative" (like "implements")
and "imperative" styles (like "add_pattern). I feel like
"declarative" style would be appropriate in both cases.

On this I totally agree, except for one thing. Since I can't do:

interface IHash
pattern [](key)
end

...to create the interface (like class or module) I don't feel like
declarative is the way to go. I have no choice but to build interface
procedurally, so I felt that more action-oriented methods were better.

I think if Matz ever gives developers the ability to do what I did above
there, I would switch to something more declarative.

Sean O'Dell
 
M

Mark Hubbart

It's not proper English, but perhaps implement? is more "Ruby."

After being annoyed with what looked like improper English in
all those #foo? methods, I noticed that you could read them as, say,
"does foo include?" or "does foo respond_to?" etc... They make sense
that way, and are proper English, inasmuch as any programming construct
is proper English :)

cheers,
Mark
 
S

Sean O'Dell

After being annoyed with what looked like improper English in
all those #foo? methods, I noticed that you could read them as, say,
"does foo include?" or "does foo respond_to?" etc... They make sense
that way, and are proper English, inasmuch as any programming construct
is proper English :)

Interesting way to look at it.

Running my eyes over a lot of Ruby methods, I can see that implement? is more
correct. However, I noticed that some methods are has_ or is_ prepended to
them. I guess when does_ makes more sense, the rule is to eliminate it?
Either way, I see the pattern and I prefer the library behave the way people
"expect" so I'll change the method names.

Just a side-note: I've added pre-defined interfaces for IArray, IHash,
IString, IDate and ITime so far. 0.6 will have ITime and IDate and whatever
others I add. 0.5 already has those others.

Sean O'Dell
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,774
Messages
2,569,599
Members
45,170
Latest member
Andrew1609
Top