Modifying Struct members

Is it possible to modify the members of a Struct class once it’s been
created, yet retain all other behavior?

An example:

Thing = Struct.new(:a, :b, :c) do
def some_method
end
end

Is there a way to modify Thing so it behaves as if it were effectively
created like so?

Thing = Struct.new(:a, :b, :c, :d) do
def some_method
end
end

Re-assigning Thing to a new Struct obviously doesn’t work because then
the behavior is lost.
Altering Thing.members seemed promising, but that didn’t work either.
Adding attr_accessor for :d isn’t good because I need Thing.new[‘d’]
to work.

Thoughts?

Yossef M. wrote:

Is there a way to modify Thing so it behaves as if it were effectively
Adding attr_accessor for :d isn’t good because I need Thing.new[‘d’]
to work.

Thoughts?


-yossef

OpenStruct might be what you want:
http://ruby-doc.org/core/classes/OpenStruct.html

-Justin

On Jul 31, 12:42 pm, Justin C. [email protected] wrote:

OpenStruct might be what you want:http://ruby-doc.org/core/classes/OpenStruct.html

I appreciate the thought, but it kind of doesn’t matter what I might
want. I have to stick with the crap I’m building on because I don’t
feel like rewriting a gem.

In case anyone is wondering, the ‘solution’ (as in it works but I
don’t recommend it) with help from Aaron “tenderlove” Patterson, goes
something like this.

Thing = Struct.new(:a, :b, :c) do
  def some_thing
  end
end

class Thing
  attr_accessor :d

  alias_method :old_brackets, :[]
  def [](m)
    if m.to_s == 'd'
      self.d
    else
      old_brackets(m)
    end
  end

  alias_method :old_brackets_equal, :[]=
  def []=(m, v)
    if m.to_s == 'd'
      self.d = v
    else
      old_brackets_equal(m, v)
    end
  end
end

Tell me that doesn’t make you unhappy.

On Jul 31, 2009, at 11:15, Yossef M. wrote:

On Jul 31, 12:42 pm, Justin C. [email protected] wrote:

OpenStruct might be what you want:http://ruby-doc.org/core/classes/OpenStruct.html

I appreciate the thought, but it kind of doesn’t matter what I might
want. I have to stick with the crap I’m building on because I don’t
feel like rewriting a gem.

What gem? You can’t submit a patch? Contact the author?

On Jul 31, 1:57 pm, Eric H. [email protected] wrote:

What gem? You can’t submit a patch? Contact the author?

It’s the freshbooks gem, and I haven’t had luck with the author.

Or, conversely, I haven’t had the patience to get the gem/author up to
snuff, and gave up about a year ago.

On Jul 31, 2009, at 09:50 , Yossef M. wrote:

Is there a way to modify Thing so it behaves as if it were effectively
created like so?

Thing = Struct.new(:a, :b, :c, :d) do
def some_method
end
end

I know this makes me a really bad person for doing this… but hey…
that’s what I’m here for. To take one for the team. I’m thinking of
releasing 2 gems off of this idea. One for thaw and the other for
struct… Hrm… there is still a problem with structs made before the
additional member is added. I’ll work that out, but I have to go to my
workout now.

#!/usr/bin/ruby -w

require ‘rubygems’
require ‘inline’

class Object
inline :C do |builder|
builder.c “VALUE thaw() { FL_UNSET(self, FL_FREEZE); return
self; }”
end
end

class Struct
inline :C do |builder|
builder.c_singleton "
void raw_members() {
return rb_struct_s_members(self);
}
"

 builder.c_singleton '
    void set_size(long n) {
      rb_iv_set(self, "__size__", LONG2NUM(n));
    }
 ', :method_name => :size=

end

def self.add_member name
self.raw_members.thaw
self.raw_members << name

 attr_accessor name

 self.size = self.raw_members.size

end
end

Thing = Struct.new(:a, :b, :c) do
def some_thing
end
end

t = Thing.new(1, 2, 3)

p [t.a, t.b, t.c]

=> [1, 2, 3]

Thing.add_member :d

t.d = 4

p [t.a, t.b, t.c, t.d]

=> [1, 2, 3, 4]

here we go:

#!/usr/bin/ruby -w

require ‘rubygems’
require ‘inline’

class Object
inline :C do |builder|
builder.c “VALUE thaw() { FL_UNSET(self, FL_FREEZE); return
self; }”
end
end

class Struct
inline :C do |builder|
builder.c_singleton "
void raw_members() {
return rb_struct_s_members(self);
}
"

 builder.c_singleton '
    void set_s_size(long n) {
      rb_iv_set(self, "__size__", LONG2NUM(n));
    }
 ', :method_name => :size=

 builder.c '
    void set_size(long n) {
      RSTRUCT(self)->len = n;
    }
 ', :method_name => :size=

end

def self.add_member name
self.raw_members.thaw
self.raw_members << name

 # a bit hacky, but works
 self.send(:define_method, name)       {     self[name]     }
 self.send(:define_method, "#{name}=") { |o| self[name] = o }

 new_size =  self.raw_members.size
 self.size = new_size

 ObjectSpace.each_object(self) do |o|
   o.size = new_size
 end

end
end

Thing = Struct.new(:a, :b, :c) do
def some_thing
end
end

t = Thing.new(1, 2, 3)

p [t.a, t.b, t.c]

=> [1, 2, 3]

Thing.add_member :d

t.d = 4 # t[‘d’] = 4 works just as well now

p((‘a’…‘d’).map { |c| t[c] })

=> [1, 2, 3, 4]

p((‘a’…‘d’).map { |c| t.send c })

=> [1, 2, 3, 4]

On Jul 31, 2009, at 17:27, MenTaLguY wrote:

…am I missing something, or don’t you also need to reallocate the
array?

That part is done in ruby code using Array#<<

On Friday 31 July 2009 02:15:49 pm Yossef M. wrote:

In case anyone is wondering, the ‘solution’ (as in it works but I
don’t recommend it) with help from Aaron “tenderlove” Patterson, goes
something like this.
[snip]
Tell me that doesn’t make you unhappy.

It doesn’t. The fact that you can monkeypatch at all makes me very happy
to be
using Ruby.

…am I missing something, or don’t you also need to reallocate the
array?

-mental