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