开发者

ruby hash within hash and a singleton method- cant access instance variable

开发者 https://www.devze.com 2023-01-20 05:37 出处:网络
#!/usr/bin/env ruby # this is the data I have @data = { :student => { :id => \'123477\', :first_name => \'Lazlo\',
#!/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开发者_如何学Go_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


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


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



#!/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.


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


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.


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

0

精彩评论

暂无评论...
验证码 换一张
取 消