一、需求
最近在做数据同步的应用,需要弄一个配置文件app.yml来进行应用的设置,例如:
work_type: target
config:
name: bj-projects
tables:
- projects.employees
- info.cities
读取出来很简单:
yml_file = 'config/app.yml'
configs = ActiveSupport::HashWithIndifferentAccess.new(YAML.load(IO.read(yml_file)))
# => {"work_type"=>"target", "config"=>{"name"=>"bj-projects", "tables"=>["projects.employees", "info.cities"]}}
使用的时候是这样的调用方式:
configs[:work_type], configs[:source][:config]
这种Hash的方式觉得很别扭,为什么不可以用这样的方式呢?
Config.work_type, Config.source
于是便发现了Struct和OpenStruct对象。
二、Struct类
Struct在 Ruby 中是用纯 C语言来实现的, 用于快速声明一个 Class, 例如:
dog = Struct.new("Dog", :name, :age)
fred = dog.new("fred", 5)
fred.age=6
printf "name:%s age:%d", fred.name, fred.age
等于你可以用Struct来迅速定义一个具有固定的属性的对象,在我的需求里面可以这样来解决:
configs = Struct.new('Config', :work_type, :source)
Config = configs.new(....)
感觉还是有点繁琐,不那么智能,而且对于没有一开始指定的属性,就不能使用了,例如:
Config.name #会报错
三、OpenStruct类
于是今天的主角OpenStruct需要亮相了。
An OpenStruct is a data structure, similar to a Hash, that allows the definition of arbitrary attributes with their accompanying values. This is accomplished by using Ruby’s metaprogramming to define methods on the class itself.
OpenStruct是一个类似于Hash的数据结构,它可以允许定义任意的具有赋值的属性。
require 'ostruct'
person = OpenStruct.new
person.name = "John Smith"
person.age = 70
person.pension = 300
puts person.name # -> "John Smith"
puts person.age # -> 70
puts person.address # -> nil
很神奇的地方,就是你可以随意指定它的属性,一旦赋值这个属性方法就存在了。
另外一个很方便的定义方法,即通过Hash来进行定义:
australia = OpenStruct.new(:country => "Australia", :population => 20_000_000)
p australia # -> <OpenStruct country="Australia" population=20000000>
直接把Hash中的键值转换为类的属性。这就是我们需要的方法了。
yml_file = 'config/app.yml'
configs = ActiveSupport::HashWithIndifferentAccess.new(YAML.load(IO.read(yml_file)))
Config = OpenStruct.new(configs)
Config.work_type # => target
Config.source # => {name: ....}
# 然后还可以随意自行添加方法
Config.name = Config.source['name']