Modifying Struct members

Y

Yossef Mendelssohn

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?
 
J

Justin Collins

Yossef said:
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?

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

-Justin
 
Y

Yossef Mendelssohn

ruct.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 =3D Struct.new:)a, :b, :c) do
def some_thing
end
end

class Thing
attr_accessor :d

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

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

Tell me that doesn't make you unhappy.
 
E

Eric Hodel

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?
 
R

Ryan Davis

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

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]
 
Y

Yossef Mendelssohn

What gem? =A0You can't submit a patch? =A0Contact 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.
 
R

Ryan Davis

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]
 
D

David Masover

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.
 

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,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top