如何在YAML文件中指定标记

时间:2014-03-10 15:29:50

标签: ruby-on-rails ruby yaml

我有一个YAML文件将由两台不同的机器解析,因此我想在文件中指定某种标记,以指示哪台机器有权读取特定块。 作为一个例子,我希望机器1解析块1,机器2解析块2:

 # BLOCK 1 - Machine 1
 -
  :id: 1234
  :worker: Foo1
  :opts:
    :ftpaccount: user1
    :limit: 10

# BLOCK 2 - Machine 2
 -
  :id: 5678
  :worker: Foo2
  :opts:
    :ftpaccount: user2
    :limit: 10

我怎样才能实现这样的目标?你如何实现类似的东西?感谢。

2 个答案:

答案 0 :(得分:3)

将块视为哈希条目,密钥为主机名:

require 'yaml'

yaml = <<EOT
host1:
  # BLOCK 1 - Machine 1
  -
    :id: 1234
    :worker: Foo1
    :opts:
      :ftpaccount: user1
      :limit: 10

host2:
  # BLOCK 2 - Machine 2
  -
    :id: 5678
    :worker: Foo2
    :opts:
      :ftpaccount: user2
      :limit: 10

EOT

config = YAML.load(yaml)
# => {"host1"=>
#      [{:id=>1234,
#        :worker=>"Foo1",
#        :opts=>{:ftpaccount=>"user1", :limit=>10}}],
#     "host2"=>
#      [{:id=>5678,
#        :worker=>"Foo2",
#        :opts=>{:ftpaccount=>"user2", :limit=>10}}]}

此时你可以抓住你需要的块:

config['host1']
# => [{:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}}]

config['host2']
# => [{:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}]

您甚至不需要对主机名进行硬编码;您可以询问机器的名称:

`hostname`.chomp # => "MyHost"

实际上,我会稍微改变YAML,所以这是哈希的散列。因此,您的YAML返回哈希数组的哈希值,因为该数组使得使用起来更加笨拙:

host1:
  # BLOCK 1 - Machine 1
  :id: 1234
  :worker: Foo1
  :opts:
    :ftpaccount: user1
    :limit: 10

host2:
  # BLOCK 2 - Machine 2
  :id: 5678
  :worker: Foo2
  :opts:
    :ftpaccount: user2
    :limit: 10

结果:

config = YAML.load(yaml)
# => {"host1"=>
#      {:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}},
#     "host2"=>
#      {:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}}

config['host1']
# => {:id=>1234, :worker=>"Foo1", :opts=>{:ftpaccount=>"user1", :limit=>10}}

config['host2']
# => {:id=>5678, :worker=>"Foo2", :opts=>{:ftpaccount=>"user2", :limit=>10}}

最后,如果您的YAML文件很复杂,或者很长,或者有重复的部分,请认真考虑编写为您发出该文件的代码。 Ruby使得以自动使用别名的非常智能的方式生成YAML变得非常容易。例如:

require 'yaml'

SOME_COMMON_DATA = {
  'shared_db_dsn' => 'mysql://user:password@host/db'
}

HOST1 = 'foo.com'
HOST1_DATA = {
  HOST1 => {
    'id' => 1234,
    'worker' => 'Foo1',
    'opts' => {
      'ftpaccount' => 'user1',
      'limit' => 10
    },
    'dsn' => SOME_COMMON_DATA
  }
}

HOST2 = 'bar.com'
HOST2_DATA = {
  HOST2 => {
    'id' => 5678,
    'worker' => 'Foo2',
    'opts' => {
      'ftpaccount' => 'user2',
      'limit' => 10
    },
    'dsn' => SOME_COMMON_DATA
  }
}

data = {
  HOST1 => HOST1_DATA,
  HOST2 => HOST2_DATA,
}

puts data.to_yaml
# >> ---
# >> foo.com:
# >>   foo.com:
# >>     id: 1234
# >>     worker: Foo1
# >>     opts:
# >>       ftpaccount: user1
# >>       limit: 10
# >>     dsn: &1
# >>       shared_db_dsn: mysql://user:password@host/db
# >> bar.com:
# >>   bar.com:
# >>     id: 5678
# >>     worker: Foo2
# >>     opts:
# >>       ftpaccount: user2
# >>       limit: 10
# >>     dsn: *1

注意YAML如何将“dsn”转换为别名,并使用锚点在第二个主机的定义中引用它。这可以节省大量空间,具体取决于您如何定义变量和构建数据结构。有关详细信息,请参阅“Aliases and Anchors”。

另外,我强烈建议避免使用哈希键的符号。通过这样做,你的YAML可以很容易地被其他语言加载,而不仅仅是Ruby。此时,您的YAML在构建大型系统时变得更加有用。

答案 1 :(得分:1)

这是一个简单的状态机,它根据yaml文件中最新的匹配注释组装一个字符串。然后将YAML字符串加载到解析器中。如果您的文件非常大,您可以轻松修改它以使用Tempfile或其他IO类。

require 'yaml'

class YAMLSplitter
  attr_reader :flag, :mode, :raw
  def initialize(flag)
    @flag = flag
    @mode = :match
    @raw = ""
  end

  def parse(file)
    File.read(file).each_line do |line|
      process_line(line)
    end

    YAML.load(raw)
  end

  private

    def process_line(line)
      set_match_status(line)
      write_line(line) if match?
    end

    def set_match_status(line)
      if line.start_with?("#")
        if line.match(flag)
          match!
        else
          nomatch!
        end
      end
    end

    def write_line(line)
      puts "WRITE_LINE #{mode.inspect} #{line.inspect}"
      raw << line
    end

    def match?
      mode == :match
    end

    def match!
      @mode = :match
    end

    def nomatch!
      @mode = :nomatch
    end

end

YAML:

---
# machine 1
- 1
- 2
- 3
- 4
# machine 2
- 5
- 6
- 7
- 8
- 9
- 10
- 11
# machine 1
- 12

执行:

splitter = YAMLSplitter.new('machine 1')
yaml = splitter.parse('test.yml')