module Plist::Emit

Create a plist

You can dump an object to a plist in one of two ways:

The following Ruby classes are converted into native plist types:

Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time, true, false

For detailed usage instructions, refer to USAGE and the methods documented below.

Constants

DEFAULT_INDENT

Public Class Methods

dump(obj, envelope = true, options = {}) click to toggle source

The following Ruby classes are converted into native plist types:

Array, Bignum, Date, DateTime, Fixnum, Float, Hash, Integer, String, Symbol, Time

Write us (via RubyForge) if you think another class can be coerced safely into one of the expected plist classes.

IO and StringIO objects are encoded and placed in <data> elements; other objects are Marshal.dump'ed unless they implement to_plist_node.

The envelope parameters dictates whether or not the resultant plist fragment is wrapped in the normal XML/plist header and footer. Set it to false if you only want the fragment.

# File lib/plist/generator.rb, line 47
def self.dump(obj, envelope = true, options = {})
  options = { :indent => DEFAULT_INDENT }.merge(options)
  output = plist_node(obj, options)

  output = wrap(output) if envelope

  return output
end
save_plist(obj, filename, options = {}) click to toggle source

Writes the serialized object's plist to the specified filename.

# File lib/plist/generator.rb, line 57
def self.save_plist(obj, filename, options = {})
  options = { :indent => DEFAULT_INDENT }.merge(options)
  File.open(filename, 'wb') do |f|
    f.write(obj.to_plist(true, options))
  end
end

Private Class Methods

comment(content) click to toggle source
# File lib/plist/generator.rb, line 126
def self.comment(content)
  return "<!-- #{content} -->\n"
end
element_type(item) click to toggle source
# File lib/plist/generator.rb, line 164
def self.element_type(item)
  case item
  when String, Symbol
    'string'

  when Integer
    'integer'

  when Float
    'real'

  else
    raise "Don't know about this data type... something must be wrong!"
  end
end
plist_node(element, options = {}) click to toggle source
# File lib/plist/generator.rb, line 65
def self.plist_node(element, options = {})
  options = { :indent => DEFAULT_INDENT }.merge(options)
  output = ''

  if element.respond_to? :to_plist_node
    output << element.to_plist_node
  else
    case element
    when Array
      if element.empty?
        output << "<array/>\n"
      else
        output << tag('array', '', options) {
          element.collect {|e| plist_node(e, options)}
        }
      end
    when Hash
      if element.empty?
        output << "<dict/>\n"
      else
        inner_tags = []

        element.keys.sort_by{|k| k.to_s }.each do |k|
          v = element[k]
          inner_tags << tag('key', CGI.escapeHTML(k.to_s), options)
          inner_tags << plist_node(v, options)
        end

        output << tag('dict', '', options) {
          inner_tags
        }
      end
    when true, false
      output << "<#{element}/>\n"
    when Time
      output << tag('date', element.utc.strftime('%Y-%m-%dT%H:%M:%SZ'), options)
    when Date # also catches DateTime
      output << tag('date', element.strftime('%Y-%m-%dT%H:%M:%SZ'), options)
    when String, Symbol, Integer, Float
      output << tag(element_type(element), CGI.escapeHTML(element.to_s), options)
    when IO, StringIO
      element.rewind
      contents = element.read
      # note that apple plists are wrapped at a different length then
      # what ruby's base64 wraps by default.
      # I used #encode64 instead of #b64encode (which allows a length arg)
      # because b64encode is b0rked and ignores the length arg.
      data = "\n"
      Base64.encode64(contents).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
      output << tag('data', data, options)
    else
      output << comment('The <data> element below contains a Ruby object which has been serialized with Marshal.dump.')
      data = "\n"
      Base64.encode64(Marshal.dump(element)).gsub(/\s+/, '').scan(/.{1,68}/o) { data << $& << "\n" }
      output << tag('data', data, options)
    end
  end

  return output
end
tag(type, contents = '', options = {}, &block) click to toggle source
# File lib/plist/generator.rb, line 130
def self.tag(type, contents = '', options = {}, &block)
  options = { :indent => DEFAULT_INDENT }.merge(options)
  out = nil

  if block_given?
    out = IndentedString.new(options[:indent])
    out << "<#{type}>"
    out.raise_indent

    out << block.call

    out.lower_indent
    out << "</#{type}>"
  else
    out = "<#{type}>#{contents.to_s}</#{type}>\n"
  end

  return out.to_s
end
wrap(contents) click to toggle source
# File lib/plist/generator.rb, line 150
def self.wrap(contents)
  output = ''

  output << '<?xml version="1.0" encoding="UTF-8"?>' + "\n"
  output << '<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' + "\n"
  output << '<plist version="1.0">' + "\n"

  output << contents

  output << '</plist>' + "\n"

  return output
end

Public Instance Methods

save_plist(filename, options = {}) click to toggle source

Helper method for injecting into classes. Calls Plist::Emit.save_plist with self.

# File lib/plist/generator.rb, line 34
def save_plist(filename, options = {})
  options = { :indent => DEFAULT_INDENT }.merge(options)
  Plist::Emit.save_plist(self, filename, options)
end
to_plist(envelope = true, options = {}) click to toggle source

Helper method for injecting into classes. Calls Plist::Emit.dump with self.

# File lib/plist/generator.rb, line 28
def to_plist(envelope = true, options = {})
  options = { :indent => DEFAULT_INDENT }.merge(options)
  return Plist::Emit.dump(self, envelope, options)
end