How do you know that it isn't able to add :email?

ratdog:tmp mike$ pry
[1] pry(main)> require 'ostruct'
=3D> false
[2] pry(main)> contact =3D OpenStruct.new(first_name: "John", last_name: =
"Smith", phone: "XXXXXXX")
=3D> #<OpenStruct first_name=3D"John", last_name=3D"Smith", =
phone=3D"XXXXXXX">
[3] pry(main)> ls contact
OpenStruct#methods:
  =3D=3D  []=3D           each_pair  hash     marshal_dump  =
method_missing  to_s
  []  delete_field  eql?       inspect  marshal_load  to_h
self.methods: first_name  first_name=3D  last_name  last_name=3D  phone  =
phone=3D
instance variables: @table

We see that contact's methods unique to itself are the expected =
setters/getters for first_name, last_name, and phone. Let's send the =
new_ostruct_member message:

[4] pry(main)> contact.send(:new_ostruct_member ,:email)
=3D> :email
[5] pry(main)> ls contact
OpenStruct#methods:
  =3D=3D  []=3D           each_pair  hash     marshal_dump  =
method_missing  to_s
  []  delete_field  eql?       inspect  marshal_load  to_h
self.methods:
  email  email=3D  first_name  first_name=3D  last_name  last_name=3D  =
phone  phone=3D
instance variables: @table

Now we wee that contact's methods include email and email=3D so it looks =
like new_ostruct_member did its metaprogramming (see =
http://www.ruby-doc.org/stdlib-2.0/libdoc/ostruct/rdoc/OpenStruct.html#met=
hod-i-new_ostruct_member for the docs).

Maybe it was the lack of an email=3Dnil or something in the output of p =
which made you suspect nothing had happened? If so we can look at the =
implementation of inspect to see how it works:

[6] pry(main)> $ contact.inspect

From: /Users/mike/.rbenv/versions/2.0.0-p195/lib/ruby/2.0.0/ostruct.rb @ =
line 23
Owner: OpenStruct
Visibility: public
Number of lines: 21

def inspect
  str =3D "#<#{self.class}"

  ids =3D (Thread.current[InspectKey] ||=3D [])
  if ids.include?(object_id)
    return str << ' ...>'
  end

  ids << object_id
  begin
    first =3D true
    for k,v in @table
      str << "," unless first
      first =3D false
      str << " #{k}=3D#{v.inspect}"
    end
    return str << '>'
  ensure
    ids.pop
  end
end

So it looks like email will not show up until it has been added to =
@table - looking at email=3D we can see how it works, going through =
modifiable:

[7] pry(main)> $ contact.email=3D

From: /Users/mike/.rbenv/versions/2.0.0-p195/lib/ruby/2.0.0/ostruct.rb @ =
line 170:
Owner: #<Class:#<OpenStruct:0x007fbe0e567470>>
Visibility: public
Number of lines: 1

define_singleton_method("#{name}=3D") { |x| modifiable[name] =3D x }
[8] pry(main)> $ contact.modifiable

From: /Users/mike/.rbenv/versions/2.0.0-p195/lib/ruby/2.0.0/ostruct.rb @ =
line 151:
Owner: OpenStruct
Visibility: protected
Number of lines: 8

def modifiable
  begin
    @modifiable =3D true
  rescue
    raise TypeError, "can't modify frozen #{self.class}", caller(3)
  end
  @table
end=20

So we can cheat and manually add an "illegal" (i.e. no getter or setter =
defined) entry to contact's @table to see if affects the output of =
inspect:

[9] pry(main)> cd contact
[10] pry(#<OpenStruct>):1> @table
=3D> {:first_name=3D>"John", :last_name=3D>"Smith", :phone=3D>"XXXXXXX"}
[11] pry(#<OpenStruct>):1> @table[:foo] =3D 'bar'
=3D> "bar"
[12] pry(#<OpenStruct>):1> cd ..
[13] pry(main)> p contact
#<OpenStruct first_name=3D"John", last_name=3D"Smith", phone=3D"XXXXXXX", =
foo=3D"bar">
=3D> #<OpenStruct first_name=3D"John", last_name=3D"Smith", =
phone=3D"XXXXXXX", foo=3D"bar">

So we can see that for an attribute to show up in the output of inspect =
it needs to have been put in the OpenStruct instance's @table.

I'll leave it to you to have a look at contact's method_missing to see =
how OpenStruct attributes might be set and retrieved so you can see the =
context in which new_ostruct_member is called.

pry is a great tool.

Hope this helps,

Mike

On 2013-06-20, at 6:09 AM, Love U Ruby <lists / ruby-forum.com> wrote:

> require 'ostruct'
>=20
> contact =3D OpenStruct.new(first_name: "John", last_name: "Smith", =
phone:
> "XXXXXXX")
>=20
> contact.send(:new_ostruct_member ,:email) #=3D "john.doe / mymail.com"
>=20
> p contact #=3D> #<OpenStruct first_name=3D"John", last_name=3D"Smith",
> phone=3D"XXXXXXX">
>=20
> Why the `#new_ostruct_member` not being able to add the `:email` ?
>=20
> --=20
> Posted via http://www.ruby-forum.com/.

--=20

Mike Stok <mike / stok.ca>
http://www.stok.ca/~mike/

The "`Stok' disclaimers" apply.