Ruby idiom for attributes / properties


#1

I’m writing a bunch of auto-marshaling code to let CLR code call back
into
Ruby. I just got the simplest possible marshaling scenario working
today,
which is letting you bind an Array of ActiveRecord objects to a CLR
DataGridView control. See
http://www.iunknown.com/articles/2006/05/03/activerecord-and-windows-formsfor
a longer discussion and a screenshot.

Bottom line is that I can now write:

data_grid.data_source = Person.find_by_last_name(‘Lam’)

Now, here’s my problem: I’m not all that happy that I’m special casing
my
code for ActiveRecord objects. However, since Ruby doesn’t have a
mechanism
for runtime-discovery of attributes, I’m special casing for ActiveRecord
since it provides an @attributes hashtable with the name-value pairs for
all
attributes in the object. This makes it easy for me to generate the CLR
anonymous class + object that implements the marshaling code that
retrieves
the attribute data from the Ruby ActiveRecord object (think about this
as
marshal by reference).

Do folks have suggestions for implementing a discoverable attribute /
property idiom in Ruby? Perhaps via a mixin module in the standard
library?
Or has this been done already and I’m just showing my Ruby newbiness?

Thanks
-John
http://www.iunknown.com


#2

The example screenshot and the amount of code is very very cool. So far
I’ve not been forced to use .net, but RubyCLR gives me a chance of al
teas not having to mess with VB or C#.

Thanks for your work on this, it’s amazing the progress that you’re
making
Kev


#3

John L. wrote:

Do folks have suggestions for implementing a discoverable attribute /
property idiom in Ruby? Perhaps via a mixin module in the standard library?
Or has this been done already and I’m just showing my Ruby newbiness?

What would you consider to be an attribute?


James B.

“A principle or axiom is of no value without the rules for applying it.”

  • Len Bullard

#4

On Thu, 4 May 2006, John L. wrote:

property idiom in Ruby? Perhaps via a mixin module in the standard library?
Or has this been done already and I’m just showing my Ruby newbiness?

Thanks
-John
http://www.iunknown.com

harp:~ > cat a.rb
require ‘traits’

class C
trait ‘a’
traits ‘b’ => 42, ‘c’ => 42.0
class_trait ‘a’
end

p C.class_traits
p C.traits

harp:~ > ruby a.rb
[[“a”, “a=”]]
[[“a”, “a=”], [“b”, “b=”], [“c”, “c=”]]

and a whole lot more

http://rubyforge.org/projects/codeforpeople/
http://codeforpeople.com/lib/ruby/traits/traits-0.8.0/README

regards.

-a


#5

Excellent!

Now the bigger question is - any chance this could become part of the
standard distribution? I could add another special case in my bridge to
marshal traits auto-magically as well, but it would rock if this
mechanism
could be something that I could grab a hold of when reflecting against
Ruby
objects at the marshaling boundary.

-John
http://www.iunknown.com


#6

On 5/4/06, James B. removed_email_address@domain.invalid wrote:

What would you consider to be an attribute?

I’m just borrowing the ActiveRecord terminology for attributes. In my
mind,
an attribute == a property. In .NET, a property is a bit of syntactical
sugar (but also a distinct metadata entity) for a getter and setter
method.

Consider this bit of C# code:

class Foo {
public string StringProperty {
get { return “string”; }
set { Console.WriteLine("setting a string to " + value); }
}
}

This generates under the covers a get_StringProperty and a
set_StringProperty method.

I would consider it equivalent to this Ruby code:

class Foo
def string_property
‘string’
end
def string_property=(value)
puts “setting a string to #{value}”
end
end

The problem is that I can’t distinguish a string_property method from
any
other method. In .NET, I can walk the property metadata to discover all
of
those properties (and those are things that can be bound to data-aware
controls in the framework).


#7

On May 4, 2006, at 1:07 AM, John L. wrote:

-John
http://www.iunknown.com

You could do

class Module
alias old_attr_reader attr_reader
alias old_attr_writer attr_writer
alias old_attr_accessor attr_accessor

def attr_reader(*args)
self.attributes |= args
old_attr_reader(*args)
end

def attr_writer(*args)
self.attributes |= args
old_attr_writer(*args)
end

def attr_accessor(*args)
self.attributes |= args
old_attr_accessor(*args)
end

def attributes
@attribs ||= []
end

def attributes=(new_attribs)
@attribs = new_attribs
end
end

class A
attr_accessor :a
end

p A.attributes

Note that you’d have to make sure this was required before anything
else, and it only works for attributes declared with the attr_*
family of methods


#8

John L. wrote:

other method. In .NET, I can walk the property metadata to discover all of
those properties (and those are things that can be bound to data-aware
controls in the framework).

RDoc considers anything declared with the attr_* class methods an
“attribute”, so that your string_property above isn’t, but this is:

class Foo
attr_accessor :string_property
end

One approach might be to hack attr_* to generate your metadata (as I see
Logan C. has now suggested).

Another approach might be to simply narrow down what valid “attribute”
methods look like. Does it have to have a foo=() and foo() pair? Does
foo() have to be parameterless, or is it accept optional parameters
(like Ara’s traits)? Then you can just pull those methods out of the
class:

class Module
def attributes
instance_methods.select do |name|
next if name =~ /!$/ || name =~ /=$/
setter = name.sub(/?$/, “”) + “=”
instance_methods.include?(setter) &&
[0, -1].include?(instance_method(name).arity) &&
[1, -2].include?(instance_method(setter).arity)
end
end
end

struct = Struct.new(:foo, :bar)
[Dir, File, IO, Hash, Thread, Struct::Tms, struct].each do |c|
p [c, c.attributes]
end

=> [Dir, [“pos”]]
[File, [“sync”, “lineno”, “pos”]]
[IO, [“sync”, “lineno”, “pos”]]
[Hash, [“default”]]
[Thread, [“abort_on_exception”, “priority”]]
[Struct::Tms, [“cutime”, “cstime”, “stime”, “utime”]]
[#Class:0x2c32660, [“bar”, “foo”]]

Cheers,
Dave


#9

On May 4, 2006, at 1:28 AM, Logan C. wrote:

Note that you’d have to make sure this was required before anything
else, and it only works for attributes declared with the attr_*
family of methods

Sorry to be replying to myself, but my code craps out with
inheritance and stuff. You’d have to do this meta-programming in a
much smarter way.


#10

Hi –

On Thu, 4 May 2006, John L. wrote:

I would consider it equivalent to this Ruby code:
The problem is that I can’t distinguish a string_property method from any
other method.

Why is that a problem? :slight_smile: It does demonstrate, though, that the C#
and Ruby samples are not equivalent.

In .NET, I can walk the property metadata to discover all of
those properties (and those are things that can be bound to data-aware
controls in the framework).

In general, the equivalent of a method name with the general format
get_x_property in Ruby is x, and the equivalent of set_x_property in
Ruby is x=. There’s room for debate as to whether there’s really such
a thing as an “attribute” at the language level at all in Ruby; I tend
to think not, or very nearly not, since the only support for the
concept, other than the method mechanisms that are in the language
anyway, are the attr_* methods, and to my eyes those are just
convenience methods layered on top of the language. (Then again, they
are in the language; hence the debate :slight_smile:

David


#11

Hi Dave,

Duck typing for attributes - cool idea, but I worry about it because of
the
difficulty (impossibility) in telling read-only properties from ordinary
methods:

module Utilities
def format_disk
end
end

class Person
include Utilities
def first_name
end
end

You see my interest in this topic is driven entirely around my ability
to
add some runtime magic in my marshaler. Since the metadata doesn’t exist
in
Ruby, I have to rely on some kind of convention. I might need to add
that
convention via a mixin that you can write that serves as an adapter
between
the type that you want to make data-bindable in .NET. So you could patch
ActiveRecord in a RubyCLR app via:

class ActiveRecord
include RubyClr::MakeBindable
end

This removes the hard-coded depenency on ActiveRecord from my marshaler.

Does this sound reasonable?

Thanks
-John
http://www.iunknown.com


#12

For more detail, see my reply to Dave B., but it’s all about being
able to
generically marshal the ‘attributes’ of a type in RubyCLR.

Cheers,
-John
http://www.iunknown.com


#13

On May 4, 2006, at 9:35 AM, John L. wrote:

end
add some runtime magic in my marshaler. Since the metadata doesn’t
include RubyClr::MakeBindable

I personally would much rather have RubyClr::MakeBindable then have
it try to ‘guess’.