Ruby idiom for attributes / properties

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

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

···

On Thu, 4 May 2006, John Lam wrote:

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

--
be kind whenever possible... it is always possible.
- h.h. the 14th dali lama

John Lam 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 Britt

"A principle or axiom is of no value without the rules for applying it."
   - Len Bullard

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

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).

···

On 5/4/06, James Britt <james_b@neurogami.com> wrote:

What would you consider to be an attribute?

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

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

···

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

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

John Lam 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.
...
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).

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 Capaldo 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

···

On 5/4/06, James Britt <james_b@neurogami.com> wrote:

Hi --

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

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

···

On Thu, 4 May 2006, John Lam wrote:

On 5/4/06, James Britt <james_b@neurogami.com> wrote:

--
David A. Black (dblack@wobblini.net)
* Ruby Power and Light, LLC (http://www.rubypowerandlight.com)
   > Ruby and Rails consultancy and training
* Author of "Ruby for Rails" from Manning Publications!
   > Paper version coming in early May! http://rubyurl.com/DDZ

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.

···

On May 4, 2006, at 1:28 AM, Logan Capaldo 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

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

···

On 5/4/06, Dave Burt <dave@burt.id.au> wrote:

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

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

Cheers,
-John

···

On 5/4/06, dblack@wobblini.net <dblack@wobblini.net> wrote:

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

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

···

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

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