I have a YAML file which maps a bunch of properties to a class. I'd like to be able to loop through all of the properties from my YAML file and set them on an object dynamically.
How can I do this?
Here's the gist of what I have so far:
YAML contents:
my_obj:
:username: 'myuser'
:password: 'mypass'
...
Ruby:
settings = YAML::load_file SETTINGS_FILE
settings = settings['my_obj']
settings.each do |s|
#psuedo-code
#example: my_obj.username = settings[:username] if my_obj.has_property?(:username)
#my_obj.[s] = settings[:s] if my_obj.has_property?(:s)
end
I'm not sure if doing this is necessarily a best practice, but there are a lot of properties and I thought this would be a cleaner way than manually setting each property directly.
Depending on what you actually need, you may be able to use Ruby's default YAML dump and load behaviour. When dumping an arbitrary object to YAML, Psych (the Yaml parser/emitter in Ruby) will look at all the instance variables of the object and write out a Yaml mapping with them. For example:
class MyObj
def initialize(name, password)
@name = name
@password = password
end
end
my_obj = MyObj.new('myuser', 'mypass')
puts YAML.dump my_obj
will print out:
--- !ruby/object:MyObj
name: myuser
password: mypass
You can then load this back with YAML.load
and you will get an instance of MyObj
with the same instance variables as your original.
You can override or customise this behaviour by implementing encode_with
or init_with
methods on your class.
As per your sample yaml file content,you would get the below Hash
.
require 'yaml'
str = <<-end
my_obj:
:username: 'myuser'
:password: 'mypass'
end
settings = YAML.load(str)
# => {"my_obj"=>{:username=>"myuser", :password=>"mypass"}}
settings['my_obj'][:username] # => "myuser"
settings['my_obj'][:password] # => "mypass"
You'll see this used a lot in Ruby gems and Rails. Here's a dummy class:
require 'yaml'
class MyClass
attr_accessor :username, :password
def initialize(params)
@username, @password = params.values_at(*[:username, :password])
end
end
We can load the data in using YAML's load_file
:
data = YAML.load_file('test.yaml') # => {"my_obj"=>{:username=>"myuser", :password=>"mypass"}}
Passing the structure just read, which in this case is a hash, and which, oddly enough, or maybe even magically, is what the class initializer takes, creates an instance of the class:
my_class = MyClass.new(data['my_obj'])
We can see the instance variables are initialized correctly from the contents of the YAML file:
my_class.username # => "myuser"
my_class.password # => "mypass"
This appears to be working. It allows me to set attributes on my_obj
dynamically from the YAML settings hash.
settings.each do |key, value|
my_obj.instance_variable_set("@#{key}", value) if my_obj.method_defined?("#{key}")
end
my_obj
has attr_accessor :username, :password
, etc.
The snippet above appears to let me set these attributes dynamically using instance_variable_set
, while also ensuring that my_obj
has the attribute I'm trying to assign with method_defined?
.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.