Class: Plist4r::Plist

Inherits:
Object show all
Defined in:
lib/plist4r/plist.rb

Overview

See README and InfoPlistExample for usage examples. Also see EditingPlistFiles

Constant Summary collapse

OptionsHash =

Recognised keys of the options hash. Passed when instantiating a new Plist Object

%w[filename path file_format plist_type strict_keys backends from_string]
FileFormats =

The plist file formats, written as symbols.

See Also:

%w[binary xml gnustep]

Instance Method Summary collapse

Constructor Details

#initialize(*args) { ... } ⇒ Plist4r::Plist

Instantiate a new Plist4r::Plist object. We usually set our per-application defaults in Config beforehand.

Plist4r::Plist.new => #<Plist4r::Plist:0x111546c @file_format=nil, …> Plist4r::Plist.new(“example.plist”) => #<Plist4r::Plist:0x1152d1c @file_format=“xml”, …> plist_string = “{ "key1" = "value1"; "key2" = "value2"; }” Plist4r::Plist.new({ :from_string => plist_string })

=> #<Plist4r::Plist:0x11e161c @file_format="xml", ...>

plist_working_dir = ‘pwd`.strip Plist4r::Plist.new({ :filename => “example.plist”, :path => plist_working_dir, :backends => [“libxml4r”,“ruby_cocoa”]})

=> #<Plist4r::Plist:0x111546c @file_format=nil, ...>

Examples:

Create new, empty plist


Load from file


Load from string


Advanced options


Parameters:

  • filename (String)
  • options (Hash)
    • for advanced usage

Yields:

  • An optional block to instance_eval &blk, and apply an edit on creation



39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# File 'lib/plist4r/plist.rb', line 39

def initialize *args, &blk
  @hash             = ::Plist4r::OrderedHash.new
  plist_type :plist

  @strict_keys = Config[:strict_keys]
  @backends         = Config[:backends]

  @from_string      = nil
  @filename         = nil
  @file_format      = nil
  @path             = Config[:default_path]

  case args.first
  when Hash
    parse_opts args.first

  when String, Symbol
    @filename = args.first.to_s
  when nil
  else
    raise "Unrecognized first argument: #{args.first.inspect}"
  end
  
  @plist_cache ||= PlistCache.new self

  edit(&blk) if block_given?
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_sym, *args, &blk) ⇒ Object

Pass down unknown method calls to the selected plist_type, to set or return plist keys. All plist data manipulation API is called through method_missing -> PlistType -> DataMethods. plist.store “CFBundleVersion” “0.1.0”

Examples:

This will actually call DataMethods#method_missing


See Also:



337
338
339
# File 'lib/plist4r/plist.rb', line 337

def method_missing method_sym, *args, &blk
  eval "@plist_type.#{method_sym} *args, &blk"
end

Instance Method Details

#<<(*args, &blk) ⇒ Object

An alias of #edit

Examples:

plist.<< do
  store "PFReleaseVersion" "0.1.1"
end

See Also:



304
305
306
# File 'lib/plist4r/plist.rb', line 304

def << *args, &blk
  edit *args, &blk
end

#[](key) ⇒ Object

Element Reference — Retrieve the value object corresponding to the key object. If not found, returns nil

Examples:

plist["CFBundleIdentifier"]   # => "com.apple.myapp"
plist[:c_f_bundle_identifier] # => "com.apple.myapp"

Parameters:

  • key (Symbol, String)

    The plist key name, either a snake-cased symbol, or literal string

Returns:

  • The value associated with the plist key



363
364
365
# File 'lib/plist4r/plist.rb', line 363

def [] key
  @plist_type.set_or_return key
end

#[]=(key, value) ⇒ Object

Element Assignment — Assign a value to the given plist key

Examples:

plist["CFBundleIdentifier"]   = "com.apple.myapp"
plist[:c_f_bundle_identifier] = "com.apple.myapp"

Parameters:

  • key (Symbol, String)

    The plist key name, either a snake-cased symbol, or literal string

  • value

    The value to store under the plist key name



375
376
377
# File 'lib/plist4r/plist.rb', line 375

def []= key, value
  store key, value
end

#backends(backends = nil) ⇒ Array <Symbol>

An array of strings, symbols or class names which correspond to the active Plist4r::Backends for this object. The priority order in which backends are executed is determined by the in sequence array order. Defaults to Plist4r::Config plist.backends [:haml, :ruby_cocoa]

Parameters:

  • A (Array <Symbol,String>)

    new list of backends to use, in Priority order

Returns:

  • (Array <Symbol>)

    The plist’s backends, each written as a symbol. Must be a sublcass of Plist4r::Backend

See Also:



252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
# File 'lib/plist4r/plist.rb', line 252

def backends backends=nil
  case backends
  when Array
    @backends = backends.collect do |b| 
      case b
      when Symbol, String
        eval "Plist4r::Backend::#{b.to_s.camelcase}"
        b.to_sym
      when nil
      else
        raise "Backend #{b.inspect} is of unsupported type: #{b.class}"
      end
    end
  when nil
    @backends
  else
    raise "Please specify an array of valid Plist4r Backends"
  end
end

#clearObject

Clears all plist keys and their contents

Examples:

plist.clear
plist.size # => 0


477
478
479
# File 'lib/plist4r/plist.rb', line 477

def clear
  @plist_type.array_dict :unselect_all
end

#collect(&blk) ⇒ Object

Alias for #map



440
441
442
# File 'lib/plist4r/plist.rb', line 440

def collect &blk
  map &blk
end

#delete(*keys) ⇒ Object

Delete plist keys from the object. Key names can be given as either snake_case’d Symbol or camelcased String

Examples:

plist.delete :c_f_bundle_identifier

Parameters:

  • keys (Array, *args)

    The list of Plist Keys to delete unconditionally. Can be an array, or argument list



454
455
456
# File 'lib/plist4r/plist.rb', line 454

def delete *keys
  @plist_type.array_dict :unselect, *keys
end

#delete_if(*keys) { ... } ⇒ Object

Conditionally delete plist keys from the object.

Examples:

plist.delete_if "CFBundleIdentifier"
plist.delete_if { |k,v| k.length > 20 }
plist.delete_if { |k,v| k =~ /Identifier/ }

Parameters:

  • keys (Array, *args)

    The list of Plist Keys to delete unconditionally. Can be an array, or argument list

Yields:

  • Delete a key-value pair if block evaluates to true.



467
468
469
470
471
# File 'lib/plist4r/plist.rb', line 467

def delete_if *keys, &blk
  delete *keys
  @hash.delete_if &blk
  @plist_type.to_hash @hash
end

#detect_plist_typeObject

Called automatically on plist load / instantiation. This method detects the “Plist Type”, using an algorithm that stats the plist data. The plist types with the highest stat (score) is chosen to be the object’s “Plist Type”.

Returns:

  • The plist’s known type, written as a symbol. Will be a sublcass of Plist4r::PlistType. Defaults to :plist

See Also:



166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/plist4r/plist.rb', line 166

def detect_plist_type
  stat_m = {}
  stat_r = {}
  Config[:types].each do |t|
    case t
    when String, Symbol
      t = eval "::Plist4r::PlistType::#{t.to_s.camelcase}"
    when Class
      t = t
    when nil
    else
      raise "Unrecognized plist type: #{t.inspect}"
    end
    t_sym = t.to_s.gsub(/.*:/,"").snake_case.to_sym
    stat_t = t.match_stat @hash.keys

    stat_m.store stat_t[:matches], t_sym
    stat_r.store stat_t[:ratio], t_sym
  end

  most_matches = stat_m.keys.sort.last      
  if most_matches == 0
    plist_type :plist
  elsif stat_m.keys.select{ |m| m == most_matches }.size > 1
    most_matches = stat_r.keys.sort.last          
    if stat_r.keys.select{ |m| m == most_matches }.size > 1
      plist_type :plist
    else
      plist_type stat_r[most_matches]
    end
  else
    plist_type stat_m[most_matches]
  end
end

#each { ... } ⇒ Object

This is equivalent to the ruby core classes method Hash#each

Examples:

plist.each do |k,v|
  puts "key = #{k.inspect}, value = #{v.inspect}"
end

Yields:

  • A block to execute for each key, value pair in plist



523
524
525
# File 'lib/plist4r/plist.rb', line 523

def each &blk
  @hash.each &blk
end

#edit(*args, &blk) ⇒ Object

Edit a plist object. Set or return plist keys. Add or remove a selection of keys. Plist key accessor methods are snake-cased versions of the key string.

Examples:

Edit some keys and values with #[] and #store

plist.edit do
  store "PFInstance" "4982394823"
  store "PFReleaseVersion" "0.1.1"
end

plist.edit do
  new_ver = self["PFReleaseVersion"] + 0.1
  store "PFReleaseVersion" new_ver
end

Edit with implicit methods. Calls method_missing()

plist.edit do
  new_ver = p_f_release_version + 0.1
  p_f_release_version(new_ver)
end


325
326
327
328
329
# File 'lib/plist4r/plist.rb', line 325

def edit *args, &blk
  @plist_type.to_hash @hash
  instance_eval *args, &blk
  detect_plist_type if plist_type == :plist
end

#empty?Boolean

This is equivalent to the ruby core classes method Array#empty?

Returns:

  • (Boolean)


513
514
515
# File 'lib/plist4r/plist.rb', line 513

def empty?
  @hash.empty?
end

#file_format(file_format = nil) ⇒ Object

The file format of the plist file we are loading / saving. Written as a symbol. One of FileFormats. Defaults to :xml

Parameters:

  • file_format (Symbol, String) (defaults to: nil)

    Can be :binary, :xml, :gnustep

Returns:

  • The file format associated to this current plist object

See Also:

  • FileFormats


146
147
148
149
150
151
152
153
154
155
156
157
158
159
# File 'lib/plist4r/plist.rb', line 146

def file_format file_format=nil
  case file_format
  when Symbol, String
    if FileFormats.include? file_format.to_s.snake_case
      @file_format = file_format.to_s.snake_case
    else
      raise "Unrecognized plist file format: \"#{file_format.inspect}\". Please specify a valid plist file format, #{FileFormats.inspect}"
    end
  when nil
    @file_format
  else
    raise "Please specify a valid plist file format, #{FileFormats.inspect}"
  end
end

#filename(filename = nil) ⇒ Object

Set or return the filename attribute of the plist object. Used in cojunction with the #path attribute

Parameters:

  • filename (String) (defaults to: nil)

    either a relative path or absolute

Returns:

  • The plist’s filename

See Also:

  • Plist::Plist#open
  • Plist::Plist#save
  • Plist::Plist#save_as


97
98
99
100
101
102
103
104
105
106
# File 'lib/plist4r/plist.rb', line 97

def filename filename=nil
  case filename
  when String
    @filename = filename
  when nil
    @filename
  else
    raise "Please specify a filename"
  end
end

#filename_path(filename_path = nil) ⇒ Object

Set or return the combined filename+path. We use this method in the backends api as the full path to load / save

Parameters:

  • filename_path (String) (defaults to: nil)

    concactenation of both filename and path elements. Also sets the @filename and @path attributes

Returns:

  • the full, expanded path to the plist file

See Also:



129
130
131
132
133
134
135
136
137
138
139
# File 'lib/plist4r/plist.rb', line 129

def filename_path filename_path=nil
  case filename_path
  when String
    @filename = File.basename filename_path
    @path     = File.dirname  filename_path
  when nil
    File.expand_path @filename, @path    
  else
    raise "Please specify directory + filename"
  end
end

#from_string(string = nil) ⇒ Object

Reinitialize plist object from string (overwrites the current contents). Usually called from #initialize plist = Plist4r::Plist.new

=> #<Plist4r::Plist:0x11e161c @file_format=nil, ...>

plist.from_string “{ "key1" = "value1"; "key2" = "value2"; }”

=> #<Plist4r::Plist:0x11e161c @file_format="gnustep", ...>

Examples:

Load from string




73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/plist4r/plist.rb', line 73

def from_string string=nil
  case string
  when String
    plist_format = Plist4r.string_detect_format(string)
    if plist_format
      @from_string = string
      @plist_cache ||= PlistCache.new self
      @plist_cache.from_string
    else
      raise "Unknown plist format for string: #{string}"
    end
  when nil
    @from_string
  else
    raise "Please specify a string of plist data"
  end
end

#has_key?(key) ⇒ true, false

Alias of #include?

Parameters:

  • key (String, Symbol)

    The plist key name

Returns:

  • (true, false)

    True if the plist has the specified key



507
508
509
510
# File 'lib/plist4r/plist.rb', line 507

def has_key? key
  key.to_s.camelcase if key.class == Symbol
  @hash.has_key? key
end

#import_hash(hash = nil) ⇒ Object

Backend method to set or return all new plist data resulting from a backend API. Used in load operations.

Parameters:

  • hash (Plist4r::OrderedHash, nil) (defaults to: nil)

    sets the new root object. Replaces all previous plist data.

Returns:

  • If no argument given, then clears all plist data, returning the new @hash root object

See Also:



345
346
347
348
349
350
351
352
353
354
# File 'lib/plist4r/plist.rb', line 345

def import_hash hash=nil
  case hash
  when Plist4r::OrderedHash
    @hash = hash
  when nil
    @hash = ::Plist4r::OrderedHash.new
  else
    raise "Please use Plist4r::OrderedHash.new for your hashes"
  end
end

#include?(key) ⇒ true, false

Check if key exists in plist This is equivalent to the ruby core classes method Hash#include?

Parameters:

  • key (String, Symbol)

    The plist key name

Returns:

  • (true, false)

    True if the plist has the specified key



499
500
501
502
# File 'lib/plist4r/plist.rb', line 499

def include? key
  key.to_s.camelcase if key.class == Symbol
  @hash.include? key
end

#keysArray <String, Symbol>

This is equivalent to the ruby core classes method Hash#keys

Examples:

plist.keys # => ["Key1", "Key2", "Key3", "etc.."]

Returns:



544
545
546
# File 'lib/plist4r/plist.rb', line 544

def keys
  @hash.keys
end

#lengthObject

This is equivalent to the ruby core classes method Array#length

Examples:

plist.length # => 14


530
531
532
# File 'lib/plist4r/plist.rb', line 530

def length
  @hash.length
end

#map { ... } ⇒ Object

Invokes block &blk once for each key-value pair in plist. Similar to the ruby core classes Array#map. Replaces the plist keys and values with the [key,value] pairs returned by &blk. Key names can be given as either snake_case’d Symbol or camelcased String

Yields:

  • For each iteration of the block, must return a 2-element Array which is a [key,value] pair to replace the original [key,value] pair from the plist.



419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
# File 'lib/plist4r/plist.rb', line 419

def map &blk
  if block_given?
    old_hash = @hash.deep_clone
    clear

    old_hash.each do |k,v|
      pair = yield k,v
      case pair
      when Array
        store pair[0], pair[1]
      when nil
      else
        raise "The supplied block must return plist [key, value] pairs, or nil"
      end
    end
  else
    raise "No block given"
  end
end

#merge!(other_plist) ⇒ Object

Merge together plist objects. Adds the contents of other_plist to the current object, overwriting any entries of the same key name with those from other_plist. Other attributes (filename, plist_type, file_format, etc) remain unaffected

Parameters:



485
486
487
488
489
490
491
492
493
# File 'lib/plist4r/plist.rb', line 485

def merge! other_plist
  if plist_type == other_plist.plist_type
    @hash.merge! other_plist.to_hash
    @plist_type.to_hash @hash
  else
    raise "plist_type differs, one is #{plist_type.inspect}, and the other is #{plist.plist_type.inspect}"
  end
  self
end

#open(filename = nil) ⇒ Plist4r::Plist

Opens a plist file

plist = Plist4r.new plist.open(“example.plist”) => #<Plist4r::Plist:0x1152d1c @file_format=“xml”, …>

Examples:

Load from file


Parameters:

  • filename (String) (defaults to: nil)

    plist file to load. Uses the #filename attribute when nil

Returns:



292
293
294
295
296
# File 'lib/plist4r/plist.rb', line 292

def open filename=nil
  @filename = filename if filename
  raise "No filename specified" unless @filename
  @plist_cache.open
end

#parse_opts(opts) ⇒ Object

Sets up those valid (settable) plist attributes as found the options hash. Normally we dont call this method directly. Called from #initialize.

Parameters:

See Also:



276
277
278
279
280
281
282
283
# File 'lib/plist4r/plist.rb', line 276

def parse_opts opts
  OptionsHash.each do |opt|
    if opts[opt.to_sym]
      value = opts[opt.to_sym]
      self.send opt, value
    end
  end
end

#path(path = nil) ⇒ Object

Set or return the path attribute of the plist object. Pre-pended to the plist’s filename (if filename is path-relative)

Parameters:

  • path (String) (defaults to: nil)

    (must be an absolute pathname)

Returns:

  • The plist’s working path

See Also:

  • Plist::Plist#filename_path


112
113
114
115
116
117
118
119
120
121
# File 'lib/plist4r/plist.rb', line 112

def path path=nil
  case path
  when String
    @path = path
  when nil
    @path
  else
    raise "Please specify a directory"
  end
end

#plist_type(plist_type = nil) ⇒ Object

Set or return the plist_type of the current object. We can use this to override the automatic type detection.

Parameters:

Returns:

  • The plist’s known type, written as a symbol. Will be a sublcass of Plist4r::PlistType. Defaults to :plist

See Also:



206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
# File 'lib/plist4r/plist.rb', line 206

def plist_type plist_type=nil
  begin
    case plist_type
    when Class
      # unless plist_type.is_a? ::Plist4r::PlistType # .is_a? returns false in spec
      unless plist_type.ancestors.include? Plist4r::PlistType
        raise "Unrecognized Plist type. Class #{plist_type.inspect} isnt inherited from ::Plist4r::PlistType"
      end
    when Symbol, String
      plist_type = eval "::Plist4r::PlistType::#{plist_type.to_s.camelcase}"
    when nil
      return @plist_type.to_sym
    else
      raise "Please specify a valid plist class name, eg ::Plist4r::PlistType::ClassName, \"class_name\" or :class_name"
    end
    @plist_type = plist_type.new self
    return @plist_type.to_sym
  rescue
    raise "Please specify a valid plist class name, eg ::Plist4r::PlistType::ClassName, \"class_name\" or :class_name"
  end
end

#saveObject

Save plist to #filename_path

Raises:

  • (RuntimeError)

    if the #filename attribute is nil

See Also:



607
608
609
610
# File 'lib/plist4r/plist.rb', line 607

def save
  raise "No filename specified" unless @filename
  @plist_cache.save
end

#save_as(filename) ⇒ Object

Save the plist under a new filename

Parameters:

  • filename (String)

    The new file name to save as. If relative, will be appended to #path

See Also:



615
616
617
618
# File 'lib/plist4r/plist.rb', line 615

def save_as filename
  @filename = filename
  save
end

#select(*keys) { ... } ⇒ Object

Element selection - Keep selected plist keys and discard others.

Parameters:

  • keys (Array, *args)

    List of Plist Keys to keep. Can be an array, or method argument list

Yields:

  • Keep every key-value pair for which the passed block evaluates to true. Works as per the ruby core classes Hash#select method



393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
# File 'lib/plist4r/plist.rb', line 393

def select *keys, &blk
  if block_given?
    selection = @hash.select &blk
    old_hash = @hash.deep_clone
    clear
    if RUBY_VERSION >= '1.9'
      selection.each do |key,value|
        store key, value
      end
    else
      selection.each do |pair|
        store pair[0], pair[1]
      end
    end
    keys.each do |k|
      store k, old_hash[k]
    end
  else
    @plist_type.array_dict :select, *keys
  end
end

#sizeObject

This is equivalent to the ruby core classes method Array#size

plist.size # => 14


536
537
538
# File 'lib/plist4r/plist.rb', line 536

def size
  @hash.size
end

#store(key, value) ⇒ Object

Element Assignment — Assign a value to the given plist key

Examples:

plist.store "CFBundleIdentifier",   "com.apple.myapp"
plist.store :c_f_bundle_identifier, "com.apple.myapp"

Parameters:

  • key (Symbol, String)

    The plist key name, either a snake-cased symbol, or literal string

  • value

    The value to store under the plist key name



386
387
388
# File 'lib/plist4r/plist.rb', line 386

def store key, value
  @plist_type.set_or_return key, value
end

#strict_keys(bool = nil) ⇒ Object

Set or return strict_keys mode

Parameters:

  • bool (true, false) (defaults to: nil)

    If true, then raise an error for any unrecognized keys that dont belong to the #plist_type

Returns:

  • The strict_keys setting for this object

See Also:



232
233
234
235
236
237
238
239
240
241
# File 'lib/plist4r/plist.rb', line 232

def strict_keys bool=nil
  case bool
  when true,false
    @strict_keys = bool
  when nil
    @strict_keys
  else
    raise "Please specify true or false to enable / disable this option"
  end
end

#to_binaryObject

Write out a binary string representation of the plist

Looking for how to store a bytestream in CFData / NSData? See EditingPlistFiles

plist = “{ "key1" = "value1"; "key2" = "value2"; }”.to_plist plist.to_binary

=> "bplist00\322\001\002\003\004Tkey2Tkey1Vvalue2Vvalue1\b\r\022\027\036\000\000\000\000\000\000\001\001\000\000\000\000\000\000\000\005\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000%"


595
596
597
# File 'lib/plist4r/plist.rb', line 595

def to_binary
  @plist_cache.to_binary
end

#to_gnustepObject

We are missing a backend for writing out plist strings in Gnustep / Nextstep / Openstep format. Contributions appreciated.



600
601
602
# File 'lib/plist4r/plist.rb', line 600

def to_gnustep
  @plist_cache.to_gnustep
end

#to_hashPlist4r::OrderedHash

The internal data storage object for the plist data

This is a pretty standard (either ActiveSupport or Ruby 1.9) ordered hash. Key names - regular ruby strings of arbitrary length.

Values - Must only store generic Ruby objects data such as TrueClass, FalseClass, Integer, Float, String, Time, Array, Hash, and Data

Data (NSData / CFData) - see EditingPlistFiles

plist = “{ "key1" = "value1"; "key2" = "value2"; }”.to_plist plist.to_hash => “key2”=>“value2”

Returns:

See Also:



562
563
564
# File 'lib/plist4r/plist.rb', line 562

def to_hash
  @hash
end

#to_xmlString

Export the plist to xml string representation. Calls through the plist cache

plist = “{ "key1" = "value1"; "key2" = "value2"; }”.to_plist plist.to_xml

=> "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>key1</key>\n\t<string>value1</string>\n\t<key>key2</key>\n\t<string>value2</string>\n</dict>\n</plist>"

Returns:

  • (String)

    An xml string which represents the entire plist, as would be the plist xml file



583
584
585
# File 'lib/plist4r/plist.rb', line 583

def to_xml
  @plist_cache.to_xml
end

#unselect(*keys) ⇒ Object

Alias for #delete



445
446
447
# File 'lib/plist4r/plist.rb', line 445

def unselect *keys
  delete *keys
end