I'm looking for a way to add properties to my already defined c开发者_C百科lass at runtime, or better:
class Client
attr_accessor :login, :password
def initialize args = {}
self.login = args[:login]
self.password = args[:password]
end
end
But then, I have this hash
{:swift_bic=>"XXXX", :account_name=>"XXXX", :id=>"123", :iban=>"XXXX"}
and I want this hash to become part of my client instance like
client = Client.new :login => 'user', :password => 'xxxxx'
then with a miraculous magic
client @@%$%PLIM!!! {:swift_bic=>"XXXX", :account_name=>"XXXX", :id=>"123", :iban=>"XXXX"}
I would be able to access
client.swift_bic => 'XXXX'
client.account_name => 'XXXX'
client.id => 123
and I also would like to maintain a proper object structure like:
Client.new(:login => 'user', :password => 'xxxxx').inspect
#<Client:0x1033c4818 @password='xxxxx', @login='user'>
after the magic
client.inspect
#<Client:0x1033c4818 @password='xxxxx', @login='user', @swift_bic='XXXX', @account_name='XXXX' @id => '123', @iban => 'XXXX'>
which would give me a very nice and well formatted json after that
Is it possible at all?
I'm getting this hash from a webservice, so I wouldn't know if theres a new property in there, and then I would have to update my app each time they perform an upgrade on their service. So, I'm sort of trying to avoid this :/
Thanks chaps.
:)
The method_missing approach would work, but if you're going to use the accessors a lot after adding them, you might as well add them as real methods, like this:
class Client
def add_attrs(attrs)
attrs.each do |var, value|
class_eval { attr_accessor var }
instance_variable_set "@#{var}", value
end
end
end
This will make them work like normal instance variables, but restricted to just one client.
I think the best solution would be mckeed's
But here is another idea to think about. You could subclass OpenStruct if you wanted to:
require 'ostruct'
class Client < OpenStruct
def initialize args = {}
super
end
def add_methods( args = Hash.new )
args.each do |name,initial_value|
new_ostruct_member name
send "#{name}=" , initial_value
end
end
end
client = Client.new :login => 'user', :password => 'xxxxx'
client.add_methods :swift_bic=>"XXXX", :account_name=>"XXXX", :iban=>"XXXX" , :to_s => 5
client # => #<Client login="user", password="xxxxx", swift_bic="XXXX", account_name="XXXX", iban="XXXX", to_s=5>
client.swift_bic # => "XXXX"
client.account_name # => "XXXX"
There are two issues with this solution, though. OpenStruct uses method_missing, so if you define a method like id, on 1.8 it will go find the object_id instead of finding your method.
Thse second issues is that it uses some private knowledge of how OpenStruct is implemented. So it could be changed in the future, breaking this code (for the record, I checked 1.8.7 - 1.9.2 and this was compatible)
Take a look at Object.method_missing. This will be called whenever your object is called with a method that isn't defined. You can define this function and use it to check the undefined method against the name of one of your hash values. If it matches, return the hash value.
You can also define your own inspect function and generate an output string containing whatever you want it to contain.
I'm going to recommend against method_missing for this - that creates a lot of 'magic' functionality that can't be documented easily or understood without working through the method_missing body. Instead, look into OpenStruct as suggested - you could even make your own class that inherits from it, like:
class Client < OpenStruct
...
end
and you'll be able to initialize a Client with whatever hash you receive.
加载中,请稍侯......
精彩评论