#!/usr/bin/env ruby
# this is the data I have
@data = {
:student => {
:id => '123477',
:first_name => 'Lazlo',
:last_name =>'Fortunatus',
:email=>'Lazlo@fortunatus.org'
},
:contact_info => {
:telephone=>'1 415 222-2222',
:address => '123 Main St',
:city =>'Beverly Hills',
:state=>'California',
:zip_code=>90210,
:social_security_number =>'111-11-1111'
}
}
class Student
# not fully implemented - this is what I need help on.
def get_id_original
# I need this to return the value @data[:student][:id]
end
def get_city_original
# I need this to return the value @data[:contact_info][:city]
end
end
s = Student.new
# this is the original method
# how can I access the @data variable here I tried @data[:student][:id] doesnt work
# I realize that data is outside of the scope of this method. However, is there any way!
s.get_id_original
# My goal is to have a singleton method that acts exactly like get_id_original,
# but get_id_original doesn't work.
def s.id
get_id_original
end
-
Use the "101010" icon in the editor to format the code in your question – RyanHennig Oct 15 '10 at 03:00
-
The question would be more useful if you explained exactly why the external instance variable must be accessed by the Student instance. Making it work goes against the whole idea of having classes and encapsulation. Maybe if you told us what the purpose of the code was, in English and not in code, we could provide alternatives. – Kelvin Jun 01 '12 at 17:43
6 Answers
First off, your id
method actually has to go into the class.
You could try something like this:
@data = { :student => { :id => '123477'} }
class Student
attr_accessor :id
def initialize(student)
self.id = student[:id]
end
end
s = Student.new(@data[:student])
puts s.id

- 25,227
- 7
- 68
- 69
-
No. This is a singleton. Your solution works but I am trying to learn singleton methods. To see what I mean go to: http://www.contextualdevelopment.com/articles/2008/ruby-singleton – hidden Oct 15 '10 at 03:17
-
I'm glad you made that comment. If you pass the right `self` into your method you *can* get at `@data`. Interesting question. See my answer... – DigitalRoss Oct 15 '10 at 04:41
It can be done!
It didn't at first work because @data
is an instance attribute of the top level object, so even though Student
is derived from Object
the attribute isn't in the new instance.
But you can pass self
into s.id
, and so then the only thing you need to add is an accessor for the data attribute.
However, that's slightly tricky because attr_reader
et al are private class methods so you can't use them directly, and you can't (because it's private) just say self.class.attr_reader
, you have to open up Object
and do it...with these changes your program works...
@data = { :student => { :id => '123477'} }
class Student
end
s = Student.new
def s.id o
o.data[:student][:id]
#how can I access the @data variable here I tried @data[:student][:id] doesnt work
#I realize that data is outside of the scope of this method. However, is there any way!
end
class Object
attr_reader :data
end
puts s.id self

- 143,651
- 25
- 248
- 329
You really should be passing in the data object so it s
has its own reference to it.
@data = { :student => { :id => '123477'} }
class Student
attr_accessor :data
def initialize(data)
@data = data
end
end
s = Student.new(@data)
# or you can use s.data = @data
def s.id
@data[:student][:id]
end
puts s.id
A word of caution. Any modifications to @data
in the outermost scope will be reflected in s
, because both @data
variables point to the same object in memory.
But what if you don't want to modify the Student class? You can just add the accessor to s
:
@data = { :student => { :id => '123477'} }
class Student
end
s = Student.new
class << s
attr_accessor :data
end
def s.id
@data[:student][:id]
end
s.data = @data
puts s.id

- 20,119
- 3
- 60
- 68
This code does the equivalent of your own answer, with some improvements. (Only by reading that did I realize what you were trying to accomplish.) To avoid being overly complex, I tried to avoid dynamically generating method names.
#!/usr/bin/env ruby
require 'forwardable'
@data = {
:student => {
:id => '123477',
:first_name => 'Lazlo',
:last_name =>'Fortunatus',
:email=>'Lazlo@fortunatus.org'
},
:contact_info => {
:telephone=>'1 415 222-2222',
:address => '123 Main St',
:city =>'Beverly Hills',
:state=>'California',
:zip_code=>90210,
:social_security_number =>'111-11-1111'
}
}
class ContactInfo
def initialize( data )
@data = data
end
def get_telephone_override
@data[:telephone]
end
def get_city_override
@data[:city]
end
def get_state_override
@data[:state]
end
def get_zip_code_override
@data[:zip_code]
end
def get_social_security_number_override
@data[:social_security_number]
end
end
class Student
extend Forwardable # enables delegation (see ruby-doc.org's standard library)
# delegates multiple methods to @contact_info, so they can be called on Student.
# Remember to have the leading colon.
def_delegators :@contact_info,
:get_telephone_override,
:get_city_override,
:get_state_override,
:get_zip_code_override,
:get_social_security_number_override
def initialize( data )
@data = data[:student]
# this is an example of composing objects to achieve separation of concerns.
# we use delegators so ContactInfo methods are available on the Student instance.
@contact_info = ContactInfo.new(data[:contact_info])
end
def get_id_override
@data[:id]
end
def get_first_name_override
@data[:first_name]
end
def get_last_name_override
@data[:last_name]
end
def get_email_override
@data[:email]
end
end
s = Student.new(@data)
class << s
alias_method :id, :get_id_override
alias_method :first_name, :get_first_name_override
alias_method :last_name, :get_last_name_override
alias_method :email, :get_email_override
alias_method :contact_info, :get_telephone_override
alias_method :city, :get_city_override
alias_method :state, :get_state_override
alias_method :zipcode, :get_zip_code_override
alias_method :ssn, :get_social_security_number_override
end
puts s.id
puts s.first_name
puts s.last_name
puts s.email
puts s.contact_info
puts s.city
puts s.state
puts s.zipcode
puts s.ssn
I think your question would've been clearer if you posted the code as you wanted it to work. I'm going to suggest an edit.

- 20,119
- 3
- 60
- 68
#!/usr/bin/ruby @data = { :student => { :id => '123477', :first_name => 'Lazlo', :last_name =>'Fortunatus', :email=>'Lazlo@fortunatus.org' }, :contact_info => { :telephone=>'1 415 222-2222', :address => '123 Main St', :city =>'Beverly Hills', :state=>'California', :zip_code=>90210, :social_security_number =>'111-11-1111' } } class Student def initialize( data ) @data = data end def get_id_override @data[:student][:id] end def get_first_name_override @data[:student][:first_name] end def get_last_name_override @data[:student][:last_name] end def get_email_override @data[:student][:email] end def get_telephone_override @data[:contact_info][:telephone] end def get_city_override @data[:contact_info][:city] end def get_state_override @data[:contact_info][:state] end def get_zip_code_override @data[:contact_info][:zip_code] end def get_social_security_number_override @data[:contact_info][:social_security_number] end end s = Student.new(@data) def s.id get_id_override end def s.first_name get_first_name_override end def s.last_name get_last_name_override end def s.email get_email_override end def s.contact_info get_telephone_override end def s.city get_city_override end def s.state get_state_override end def s.zipcode get_zip_code_override end def s.ssn get_social_security_number_override end puts s.id puts s.first_name puts s.last_name puts s.email puts s.contact_info puts s.city puts s.state puts s.zipcode puts s.ssn
Here is the answer after some work. Anyone has a better answer than mine let me know.

- 3,216
- 8
- 47
- 69
Should you be defining an instance variable (prefixed by "@") outside of a class definition?
Also, you can't define a method with a period in the name

- 1,072
- 9
- 12