class Kvx
Kvx
does the following:
-
h -> xml
-
xml -> h
-
s -> h
Attributes
attributes[RW]
summary[RW]
to_h[R]
Public Class Methods
new(x=nil, summary: {}, body: {}, attributes: {}, comment_char: nil, debug: false)
click to toggle source
# File lib/kvx.rb, line 37 def initialize(x=nil, summary: {}, body: {}, attributes: {}, comment_char: nil, debug: false) @header = attributes.any? @identifier = 'kvx' @summary, @body = summary, body @ignore_blank_lines ||= false @attributes, @debug = attributes, debug @comment_char = comment_char h = { hash: :passthru, :'rexle::element' => :xml_to_h, string: :parse_string, rexle: :doc_to_h, :"rexle::element::value" => :parse_string } if x then sym = h[x.class.to_s.downcase.to_sym] puts 'sym: ' + sym.inspect if @debug @body = method(sym).call x end methodize(@body) end
Public Instance Methods
import(s)
click to toggle source
# File lib/kvx.rb, line 68 def import(s) @body = parse_string(s) methodize(@body) end
save(filename)
click to toggle source
# File lib/kvx.rb, line 79 def save(filename) FileX.write filename, self.to_s end
to_doc()
click to toggle source
# File lib/kvx.rb, line 104 def to_doc() a = if @summary.empty? then [self.class.to_s.downcase, @attributes, '', *make_xml(@body)] else summary = make_xml(@summary) tags_found = summary.assoc(:tags) if tags_found then tags = tags_found.pop tags_found.push *tags.split.map {|x| [:tag,{},x]} end summary = [:summary, {}, *summary] # -- use the nested description Hash object if there are multiple lines h = {} @body.each do |key, value| h[key] = value.is_a?(String) ? value : value[:description] end body = [:body, {}, *make_xml(h)] [self.class.to_s.downcase, @attributes, '', summary, body] end puts 'a: ' + a.inspect if @debug doc = Rexle.new a doc.instructions = @instructions || [] doc end
to_s()
click to toggle source
# File lib/kvx.rb, line 144 def to_s() header = '' if @header or (@summary and @summary.any?) then attr = @attributes ? ' ' + @attributes\ .map {|x| "%s='%s'" % x }.join(' ') : '' header = '<?' + @identifier header += attr header += "?>\n" if @summary and @summary.any? then header += scan_to_s @summary header += "\n----------------------------------\n\n" end end # -- use the nested description Hash object if there are multiple lines h = {} @body.each do |key, value| h[key] = if value.is_a?(String) then if value.lines.length < 2 then value else "\n" + value.lines.map {|x| ' ' + x }.join end else "\n" + value[:description].lines.map {|x| ' ' + x }.join end end header + scan_to_s(h) end
to_xml(options={pretty: true})
click to toggle source
# File lib/kvx.rb, line 186 def to_xml(options={pretty: true}) doc = self.to_doc doc.xml(options) end
to_xslt()
click to toggle source
# File lib/kvx.rb, line 193 def to_xslt() summary = self.summary.keys.map do |key| " <xsl:element name='#{key}'><xsl:value-of select='#{key}' /></xsl:element>" end.join("\n") body = self.body.keys.map do |key| " <xsl:element name='#{key}'><xsl:value-of select='#{key}' /></xsl:element>" end.join("\n") s = " <xsl:stylesheet xmlns:xsl='http://www.w3.org/1999/XSL/Transform' version='1.0'> <xsl:template match='kvx'> <xsl:element name='kvx'> <xsl:attribute name='created'> <xsl:value-of select='@created'/> </xsl:attribute> <xsl:attribute name='last_modified'> <xsl:value-of select='@last_modified'/> </xsl:attribute> <xsl:apply-templates select='summary' /> <xsl:apply-templates select='body' /> </xsl:element> </xsl:template> <xsl:template match='summary'> <xsl:element name='summary'> #{summary} </xsl:element> </xsl:template> <xsl:template match='body'> <xsl:element name='body'> #{body} </xsl:element> </xsl:template> </xsl:stylesheet> " end
update(id=nil, hpair={})
click to toggle source
used by RecordX to update a KVX record id is unnecssary because there is only 1 record mapped to RecordX
# File lib/kvx.rb, line 252 def update(id=nil, hpair={}) @body.merge! hpair end
Private Instance Methods
deep_clone(h)
click to toggle source
# File lib/kvx.rb, line 258 def deep_clone(h) h.inject({}) do |r, x| h2 = if x.last.is_a? Hash then [x.first, deep_clone(x.last)] else x end r.merge h2[0] => h2[1] end end
doc_to_h(doc)
click to toggle source
# File lib/kvx.rb, line 272 def doc_to_h(doc) xml_to_h(doc.root) end
get_attributes(raw_attributes)
click to toggle source
# File lib/kvx.rb, line 276 def get_attributes(raw_attributes) # r1 = /([\w\-:]+\='[^']*)'/ r2 = /([\w\-:]+\="[^"]*)"/ r = raw_attributes.scan(/#{r1}|#{r2}/).map(&:compact)\ .flatten.inject(Attributes.new) do |r, x| attr_name, raw_val = x.split(/=/,2) val = attr_name != 'class' ? raw_val[1..-1] : raw_val[1..-1].split r.merge(attr_name.to_sym => val) end return r end
hashify(e)
click to toggle source
# File lib/kvx.rb, line 291 def hashify(e) v = if e.has_elements? then e.elements.inject({}) do |r, x| r.merge hashify(x) end else e.text end {e.name.to_sym => v} end
make_xml(h)
click to toggle source
# File lib/kvx.rb, line 304 def make_xml(h) puts 'inside make_xml: ' + h.inspect if @debug h2 = h.clone h2.each {|key,value| value.delete :items if value.is_a?(Hash) } RexleBuilder.new(h2, debug: false).to_a[3..-1] end
methodize(h)
click to toggle source
# File lib/kvx.rb, line 333 def methodize(h) h.each do |k,v| define_singleton_method(k){v} unless self.methods.include? k unless self.methods.include? (k.to_s + '=').to_sym then define_singleton_method((k.to_s + '=').to_sym){|x| h[k] = x} end end end
parse_string(s)
click to toggle source
# File lib/kvx.rb, line 313 def parse_string(s) buffer, type = RXFReader.read(s) puts ('buffer: ' + buffer.inspect).debug if @debug if buffer.lstrip =~ /^<\?xml/ then s = buffer.force_encoding("UTF-8") doc = Rexle.new(s) @instructions = doc.instructions puts '@instructions: ' + @instructions.inspect if @debug xml_to_h(doc.root) else parse_to_h(buffer) end end
parse_to_h(s, header_pattern: %r(^<\?kvx[\s\?]))
click to toggle source
# File lib/kvx.rb, line 347 def parse_to_h(s, header_pattern: %r(^<\?kvx[\s\?])) raw_txt, _ = RXFReader.read(s) # does the raw_txt contain header information? a = s.strip.lines txt = if a[0] =~ header_pattern then raw_header = a.shift attr = get_attributes(raw_header) if attr[:created] then attr[:last_modified] = Time.now.to_s else attr[:created] = Time.now.to_s end @attributes.merge! attr @header = true body, summary = a.join.strip.split(/^----*$/).reverse @summary = scan_to_h summary if summary body else raw_txt end scan_to_h(txt) end
passthru(x)
click to toggle source
# File lib/kvx.rb, line 378 def passthru(x) if x[:summary] and x[:body] @summary = deep_clone x[:summary] deep_clone x[:body] else deep_clone x end end
pretty_print(a, indent='')
click to toggle source
# File lib/kvx.rb, line 389 def pretty_print(a, indent='') a.map do |x| (x.is_a?(String) or x.nil?) ? x.to_s : pretty_print(x, indent + ' ') end.join("\n" + indent) end
scan_to_h(txt)
click to toggle source
# File lib/kvx.rb, line 398 def scan_to_h(txt) txt.gsub!(/^\w+:(?=$)/,'\0 ') puts 'txt:' + txt.inspect if @debug # auto indent any multiline values which aren't already indented indent = '' lines = txt.gsub(/^-+$/m,'').lines.map do |line| if not line[/^ *[^:]+:|^ +/] then indent + ' ' + line else indent = line[/^ +/] || '' line end end puts ('lines: ' + lines.inspect).debug if @debug puts ('inside scan_to_h').info if @debug txt = lines.join.gsub(/^-*$/,'') if @comment_char then txt.gsub!(/(?<=\S) +#{@comment_char}.*|#{@comment_char}[^\n]+/,'') end raw_a = LineTree.new(txt.strip, ignore_blank_lines: @ignore_blank_lines).to_a puts ('raw_a: ' + raw_a.inspect).debug if @debug # if there are any orphan lines which aren't nested underneath a # label, they will be fixed using the following statement a = raw_a.chunk {|x| x[0][/^[^:]+:/]}.inject([]) do |r,y| puts 'r: ' + r.inspect if @debug if r.last and !y.first[/[^:]+:/] then r.last << y.last[-1] else puts 'y: ' + y.inspect if @debug r << y.last[-1] end r end @body = a.inject({}) do |r, line| s = line.shift puts ('s: ' + s.inspect).debug if @debug if line.join.length > 0 then puts 'line: ' + line.inspect if @debug padding = line[0].length < 2 ? "\n" : "\n " s10 = line.map{|x| x.join(padding)}.join("\n") r2 = if s10[/^ *\w+:[\n ]/] then scan_to_h(s10) else desc = pretty_print(line).split(/\n(?=\w+: )/) txt2, remaining = desc h = txt2.lines.inject([]) do |r, x| x.chomp! x.length > 0 ? r << x : r end r3 = {description: txt2, items: LineTree.new(txt2).to_a(normalize: true)} if remaining then r3.merge!(scan_to_h remaining + "\n ") end r3 end r.merge({s[/[^:]+/].to_sym => r2}) else value, name = s.split(/: */,2).reverse name ||= 'description' v = value =~ /^\{\s*\}$/ ? {} : value.to_s r.merge({name.to_sym => v}) end end puts ('@body: ' + @body.inspect).debug if @debug @body end
scan_to_s(h, indent='')
click to toggle source
# File lib/kvx.rb, line 503 def scan_to_s(h, indent='') a = h.inject([]) do |r, x| if x.last.is_a? Hash then r << x.first.to_s + ":\n" + scan_to_s(x.last, ' ') else r << "%s%s: %s" % [indent, *x] end end @to_s = a.join("\n") end
xml_to_h(node)
click to toggle source
# File lib/kvx.rb, line 516 def xml_to_h(node) puts 'node: ' + node.xml.inspect if @debug @attributes = node.attributes.to_h summary = node.element('summary') if summary then etags = summary.element 'tags' if etags then tags = etags.xpath('tag/text()') etags.delete 'tag' etags.text = tags.join(' ') if tags.any? end @summary = hashify(summary)[:summary] @body = hashify(node.element('body'))[:body] else @body = hashify node end end