Rails加载YAML进行散列和符号引用

我在Rails 3.0.9中加载一个YAML文件,如下所示:

APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__))) 

它加载所有的内容像分层哈希,没问题。 我不喜欢的部分是只能用单引号或双引号访问哈希,而不能使用符号。

 APP_CONFIG['mailer']['username'] # works fine APP_CONFIG[:mailer][:username] # doesn't 

有什么想法吗?

尝试使用HashWithIndifferentAccess之类的

 APP_CONFIG = HashWithIndifferentAccess.new(YAML.load(File.read(File.expand_path('../app.yml', __FILE__)))) 

另一种解决scheme是将你想要访问的键作为一个符号前缀冒号。 例如:

 default: &default :symbol: "Accessed via a symbol only" string: "Accessed via a string only" development: <<: *default test: <<: *default production: <<: *default 

之后你可以像这样访问它们:

 APP_CONFIG[:symbol] APP_CONFIG['string'] 

请注意,我正在使用YAML::ENGINE.yamler = "syck" 。 不知道这是否适用于psych 。 (尽pipe我在这个例子中展示了心理,但是肯定不会支持关键合并。)

关于使用HashWithIndifferentAccess使用它具有创build重复键的副作用:一个用于符号访问,一个用于string访问。 如果您将YAML数据作为数组传递,这可能是邪恶的。 如果你使用这个解决scheme,请注意这一点。

如果您正在使用Ruby on Rails,则可能需要查看symbolize_keys() ,而这正是OP所要求的。 如果散列很深,可以使用deep_symbolize_keys() 。 使用这种方法,答案是

 APP_CONFIG = YAML.load(File.read(File.expand_path('../app.yml', __FILE__))).deep_symbolize_keys 

这与选定的答案是一样的,但语法更好:

 YAML.load(File.read(file_path)).with_indifferent_access 

我在挖掘时发现了另一个可能的答案。

您可以放弃HashWithIndifferentAccess.new,而是将其添加到您的YAML文件的顶部:

 --- !map:HashWithIndifferentAccess 

然后简单的YAML.load像正常。 唯一的诀窍就是如果你在你的环境中使用初始化工具等等(就像我)那样需要加载rails。

  1. Rails有一个特殊的方法来表示键。
  2. 您可以使用load_file方法并摆脱File.read
  3. 不知道你是否也需要expand_path,默认目录是rails的根目录。

我会写这么简单:

YAML::load_file('app.yml').symbolize_keys

你可能习惯了Rails中的params哈希,这实际上是一个HashWithIndifferentAccess而不是一个标准的ruby哈希对象。 这允许你使用像“action”这样的string或像action这样的符号来访问内容。

使用HashWithIndifferentAccess,无论使用什么,您都将得到相同的结果,但请记住,这只适用于HashWithIndifferentAccess对象。

所以为了使这个工作与YAML,你将不得不加载到HashWithIndifferentAccess的YAML.load的结果,如下所示:

 APP_CONFIG = HashWithIndifferentAccess.new( YAML.load(File.read(File.expand_path('../app.yml', __FILE__))) ) 

如果你使用纯Ruby(即没有Rails),你可以中间更改为JSON格式。 JSON库的parse方法可以表示键。

http://ruby-doc.org/stdlib-2.0.0/libdoc/json/rdoc/JSON.html#method-i-parse

这是我的意思:

 JSON.parse(JSON.dump(YAML.load_file(File.expand_path('../app.yml', __FILE__))), symbolize_names: true) 

注意 :这增加了json转换的开销。