begin require 'psych' rescue LoadError require File.join(File.dirname(__FILE__), 'yaml_waml') end module I27r class Line attr_reader :text delegate :scan, :to => :@text def initialize(text, options = {}) @generated = !!options[:generated] || text.ends_with?(' #g') @yaml = YAML.load text.to_s + ' ' @text = text end def key yaml? ? @yaml.keys.first : nil end def value yaml? ? @yaml.values.first : nil end def indent yaml? ? @text.scan(/^ */).first : '' end def generated? @generated end def yaml? @yaml.is_a?(Hash) && @yaml.keys.first.is_a?(String) end def value=(val) if @yaml[self.key] != val @yaml[self.key] = val generate_text self.indent end end def to_s "#{@text}#{' #g' if generated? && yaml? && !value.nil? && !@text.ends_with?(' #g')}" end private def generate_text(indent) @text = indent + @yaml.to_yaml.sub(/--- ?\n/, '').chomp.rstrip end end class YamlDocument attr_accessor :root def initialize(yaml = '') @lines = yaml.split("\n").map {|s| Line.new s} end def self.load_yml_file(yml_path) if File.exists? yml_path self.new File.read(yml_path) else self.new end end def [](*path) find_line_by_path(path.flatten).try :value end def find_line_by_path(path, line_num = -1, add_new = false) key = path.shift indent = line_num == -1 ? '' : @lines[line_num].scan(/^ */).first + ' ' @lines[(line_num + 1)..-1].each do |line| line_num += 1 next unless line.yaml? if (line.indent == indent) && (line.key == key) if path.empty? return line else return find_line_by_path path, line_num, add_new end elsif line.indent < indent if add_new new_line = Line.new("#{indent}#{key}:", :generated => true) if @lines[line_num - 1].try(:text) == '' @lines.insert line_num - 1, new_line else @lines.insert line_num, new_line end return new_line if path.empty? return find_line_by_path path, line_num, add_new else return end end end if add_new new_line = Line.new("#{indent}#{key}:", :generated => true) @lines.insert @lines.count, new_line return new_line if path.empty? return find_line_by_path path, @lines.count - 1, add_new end end def []=(*args) value = args.pop path = args.flatten # return if value && (value == self[path]) line = find_line_by_path path.dup if line if line.generated? line.value = value end else line = find_line_by_path path, -1, true line.value = value end end def to_s(add_blank_line = false) previous_indent = '' ''.tap do |ret| @lines.each do |line| ret << "\n" if add_blank_line && (line.indent < previous_indent) && !line.to_s.blank? && !ret.ends_with?("\n\n") previous_indent = line.indent ret << line.to_s << "\n" end end end end end