Why the `#new_ostruct_member` not being able to add the `:email`?

require ‘ostruct’

contact = OpenStruct.new(first_name: “John”, last_name: “Smith”, phone:
“XXXXXXX”)

contact.send(:new_ostruct_member ,:email)

p contact #=> #<OpenStruct first_name=“John”, last_name=“Smith”,
phone=“XXXXXXX”>

Why the #new_ostruct_member not being able to add the :email ?

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

ratdog:tmp mike$ pry
[1] pry(main)> require ‘ostruct’
=> false
[2] pry(main)> contact = OpenStruct.new(first_name: “John”, last_name:
“Smith”, phone: “XXXXXXX”)
=> #<OpenStruct first_name=“John”, last_name=“Smith”, phone=“XXXXXXX”>
[3] pry(main)> ls contact
OpenStruct#methods:
== []= each_pair hash marshal_dump method_missing
to_s
[] delete_field eql? inspect marshal_load to_h
self.methods: first_name first_name= last_name last_name= phone
phone=
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)
=> :email
[5] pry(main)> ls contact
OpenStruct#methods:
== []= each_pair hash marshal_dump method_missing
to_s
[] delete_field eql? inspect marshal_load to_h
self.methods:
email email= first_name first_name= last_name last_name= phone
phone=
instance variables: @table

Now we wee that contact’s methods include email and email= so it looks
like new_ostruct_member did its metaprogramming (see

for the docs).

Maybe it was the lack of an email=nil 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 = “#<#{self.class}”

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

ids << object_id
begin
first = true
for k,v in @table
str << “,” unless first
first = false
str << " #{k}=#{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= we can see how it works, going through
modifiable:

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

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}=”) { |x| modifiable[name] = 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 = true
rescue
raise TypeError, “can’t modify frozen #{self.class}”, caller(3)
end
@table
end

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(#):1> @table
=> {:first_name=>“John”, :last_name=>“Smith”, :phone=>“XXXXXXX”}
[11] pry(#):1> @table[:foo] = ‘bar’
=> “bar”
[12] pry(#):1> cd …
[13] pry(main)> p contact
#<OpenStruct first_name=“John”, last_name=“Smith”, phone=“XXXXXXX”,
foo=“bar”>
=> #<OpenStruct first_name=“John”, last_name=“Smith”, phone=“XXXXXXX”,
foo=“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 [email protected] wrote:

Why the #new_ostruct_member not being able to add the :email ?


Posted via http://www.ruby-forum.com/.

Mike S. [email protected]
http://www.stok.ca/~mike/

The “`Stok’ disclaimers” apply.

Mike S. wrote in post #1112971:

That’s a great explanation,hope I would be able to go miles with this.
But during that course If I stuck,I would request you to help me. :))

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 9:08 AM, Love U Ruby [email protected] wrote:

Mike S. wrote in post #1112971:

That’s a great explanation,hope I would be able to go miles with this.
But during that course If I stuck,I would request you to help me. :))

One good place to get an overview of pry is
http://www.confreaks.com/videos/2467-railsconf2013-pry-the-good-parts

I have the pry-plus gem installed which bundles a load of useful extras
together. GitHub - rking/pry-plus: Pry plus the essential plugins.

Mike

Hope this helps,

Mike


Posted via http://www.ruby-forum.com/.

Mike S. [email protected]
http://www.stok.ca/~mike/

The “`Stok’ disclaimers” apply.

unknown wrote in post #1113026:

Am 20.06.2013 12:09, schrieb Love U Ruby:

require ‘ostruct’

contact = OpenStruct.new(first_name: “John”, last_name: “Smith”, phone:
“XXXXXXX”)

contact.send(:new_ostruct_member ,:email) #= “[email protected]

simply use

contact.email = ‘[email protected]

Actually I was trying to understand the intention of providing such
feature method #new_ostruct_member.

Still thanks for showing your interest.

Am 20.06.2013 20:58, schrieb Love U Ruby:

contact.email = '[email protected]'

Actually I was trying to understand the intention of providing such
feature method #new_ostruct_member.

It’s not really a “provided” feature, `new_ostruct_member’ is meant
to be “Used internally” (quoted from the docs). That’s the reason it
is not declared as a public method.

Generally, it is bad practice to rely on private/protected methods.
The public interface of a library is usually well defined and stable,
but internally used methods might change anytime without further notice.

Am 20.06.2013 12:09, schrieb Love U Ruby:

require ‘ostruct’

contact = OpenStruct.new(first_name: “John”, last_name: “Smith”, phone:
“XXXXXXX”)

contact.send(:new_ostruct_member ,:email) #= “[email protected]

simply use

contact.email = ‘[email protected]

unknown wrote in post #1113031:

Am 20.06.2013 20:58, schrieb Love U Ruby:

contact.email = '[email protected]'

Actually I was trying to understand the intention of providing such
feature method #new_ostruct_member.

thanks for the advice,I will remember!!!

Generally, it is bad practice to rely on private/protected methods.
The public interface of a library is usually well defined and stable,
but internally used methods might change anytime without further notice.