Git Product home page Git Product logo

cfpropertylist's Introduction

CFPropertyList implementation class to read, manipulate and write both XML and binary property list files (plist(5)) as defined by Apple. Have a look at CFPropertyList::List for more documentation.

Caution!

In version 3.0.0 we dropped Ruby 1.8 compatibility. If you are using Ruby 1.8 consider to update Ruby; if you can't upgrade, don't upgrade CFPropertyList.

Installation

You could either use ruby gems and install it via

gem install CFPropertyList

or you could clone this repository and place it somewhere in your load path.

Example:

require 'cfpropertylist'

If you're using Rails, you can add it into your Gemfile

gem 'CFPropertyList'

Usage

create a arbitrary data structure of basic data types

data = {
  'name' => 'John Doe',
  'missing' => true,
  'last_seen' => Time.now,
  'friends' => ['Jane Doe','Julian Doe'],
  'likes' => {
    'me' => false
  }
}

create CFPropertyList::List object

plist = CFPropertyList::List.new

call CFPropertyList.guess() to create corresponding CFType values

plist.value = CFPropertyList.guess(data)

write plist to file

plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)

… later, read it again

plist = CFPropertyList::List.new(:file => "example.plist")
data = CFPropertyList.native_types(plist.value)

Author and license

Author: Christian Kruse (mailto:[email protected])

Copyright: Copyright (c) 2010

License: MIT License

cfpropertylist's People

Contributors

0fye avatar amatsuda avatar cantino avatar ccaviness avatar ckruse avatar dreamcat4 avatar gregwebs avatar haus avatar improbability avatar joe-mcnuggets avatar micwoj92 avatar mschmidt avatar nguyen-phillip avatar ojab avatar paynerc avatar revolter avatar rgov avatar segiddins avatar shubhampathak avatar siuying avatar sjmadsen avatar smparkes avatar tboyko avatar voxik avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

cfpropertylist's Issues

invalid byte sequence in UTF-8

Hi,

I'm getting the following when running the library via xcodebuild -exportArchive process, via the ipatool.

2015-10-02 21:16:25 +0000  warning: Configuration issue: platform iPhoneOS.platform doesn't have a 'iPhoneOS9.0.sdk' SDK with a SDKSettings.plist; ignoring it. Exception: #<ArgumentError: invalid byte sequence in UTF-8>
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbPlainCFPropertyList.rb:43:in `skip'
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbPlainCFPropertyList.rb:43:in `skip_whitespaces'
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbPlainCFPropertyList.rb:177:in `import_plain'
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbPlainCFPropertyList.rb:24:in `load'
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbCFPropertyList.rb:313:in `load_str'
    /Users/slatebuilder/.rbenv/versions/2.0.0-p598/lib/ruby/gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbCFPropertyList.rb:299:in `load_binary_str'
    /Applications/Xcode.app/Contents/Developer/usr/bin/ipatool:292:in `LoadPlist'
    /Applications/Xcode.app/Contents/Developer/usr/bin/ipatool:2009:in `block in allKnownPlatforms'
    /Applications/Xcode.app/Contents/Developer/usr/bin/ipatool:1993:in `each'
    /Applications/Xcode.app/Contents/Developer/usr/bin/ipatool:1993:in `allKnownPlatforms'
    /Applications/Xcode.app/Contents/Developer/usr/bin/ipatool:2108:in `<main>'```

Any thoughts on what might be causing this?

Ruby 3.2 support

CFPropertyList doesn't work with Ruby 3.2.

Error:

 NoMethodError: undefined method `exists?' for File:Class
/.gem/ruby/3.2.0/gems/CFPropertyList-3.0.5/lib/cfpropertylist/rbCFPropertyList.rb:378:in `save'

The method File.exists? has been deprecated since Ruby 2.1.0 and was removed on Ruby 3.2.0.

invalid byte sequence in US-ASCII

<ArgumentError: invalid byte sequence in US-ASCII>

/Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.3.1/lib/cfpropertylist/rbCFPropertyList.rb:326:in `load_str'

libxml-ruby doesn't build on jRuby

Hey Christian,

Have you thought about using nokogiri for your XML work instead of dealing with the libxml-ruby gem? If you were willing to switch, your gem would then install on jRuby too :)

Regards,
David

Ruby 1.9 Time

Hi,
This appeared during my Ruby 1.9 testing:

undefined method `parse' for Time:Class

It seems that require 'date' is no longer including the Time class. I would recommend to add the following line:

require 'time'

CFPropertyList.native_types doesn't preserve Blob/raw data

Discovered this today as I was trying to do some more work with CFPropertyList. See this example code that I ran in Pry (using CFPropertyList 2.2.5) that must be run as the root user (in order to read the user's plist)

require 'CFPropertyList'

# Load any OS X 10.8 or higher User's Plist
plist_obj = CFPropertyList::List.new(:file => "/var/db/dslocal/nodes/Default/users/puppetuser.plist")

# This returns CFPropertyList::CFData
plist_obj.value.value['ShadowHashData'].value[0].class

# All the types
plist_obj.value.value.each {|k,v| puts v.value.first.class };0

# Convert to native types
hash = CFPropertyList.native_types(plist_obj.value)

# This returns a string, not CFPropertyList::CFData or CFPropertyList::Blob
hash['ShadowHashData'][0].class

# All the types - all Strings
hash.each {|k,v| puts v.first.class }

# Now let's try saving this to another plist:
new_plist = CFPropertyList::List.new
new_plist.value = CFPropertyList.guess(hash)

# This should error with Encoding::InvalidByteSequenceError
new_plist.save('/tmp/new.plist', CFPropertyList::List::FORMAT_BINARY)

EDIT: I'm a moron and can't tell the difference between CFDate and CFData. I do think I found the problem, though, and will submit a pull request momentarily...

Binary encoded plist returns <data></data> as strings instead of CFPropertyList::Blob

When you execute CFPropertyList.native_types(plist.value) on a plist which has a in it and is binary encoded, the gem returns the contents as string instead of type CFPropertyList::Blob. The issue seems to be in the initialize methods for CFData. Attached is the patch which I think should fix the problem.

Patch:
def initialize(value=nil,format=DATA_BASE64)
if(format == DATA_RAW)
@raw_value = Blob.new(value)
else
@value = value
end
end

Sample Plist:

favoriteitems Controller CustomListItems CustomListItems Alias AAAAAACQAAMAAQAAzeaRzAAASCsAAAAAAAU4rgAGHwgA AMs02NwAAAAACSD//gAAAAAAAAAA/////wABAAgABTiu AAIQvgAOABAABwBEAHIAbwBwAGIAbwB4AA8ACAADAEMA YQBiABIAFVVzZXJzL2ttdWRnYWwvRHJvcGJveAAAEwAB LwAAFQACAA7//wAA Name Dropbox

License missing from gemspec

RubyGems.org doesn't report a license for your gem. This is because it is not specified in the gemspec of your last release.

via e.g.

spec.license = 'MIT'
# or
spec.licenses = ['MIT', 'GPL-2']

Including a license in your gemspec is an easy way for rubygems.org and other tools to check how your gem is licensed. As you can imagine, scanning your repository for a LICENSE file or parsing the README, and then attempting to identify the license or licenses is much more difficult and more error prone. So, even for projects that already specify a license, including a license in your gemspec is a good practice. See, for example, how rubygems.org uses the gemspec to display the rails gem license.

There is even a License Finder gem to help companies/individuals ensure all gems they use meet their licensing needs. This tool depends on license information being available in the gemspec. This is an important enough issue that even Bundler now generates gems with a default 'MIT' license.

I hope you'll consider specifying a license in your gemspec. If not, please just close the issue with a nice message. In either case, I'll follow up. Thanks for your time!

Appendix:

If you need help choosing a license (sorry, I haven't checked your readme or looked for a license file), GitHub has created a license picker tool. Code without a license specified defaults to 'All rights reserved'-- denying others all rights to use of the code.
Here's a list of the license names I've found and their frequencies

p.s. In case you're wondering how I found you and why I made this issue, it's because I'm collecting stats on gems (I was originally looking for download data) and decided to collect license metadata,too, and make issues for gemspecs not specifying a license as a public service :). See the previous link or my blog post about this project for more information.

Prettify XML option?

Generated XML has a single line with the plist.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0"><array><array><string>woman</string><string>female</string></array><array><string>man</string><string>male</string></array></array></plist>

Is it possible to have (indented) line feeds?

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <array>
    <array>
      <string>woman</string>
      <string>female</string>
    </array>
    <array>
      <string>man</string>
      <string>male</string>
    </array>
  </array>
</plist>

Because it's easier to track diff on git, and also easier to browse the content on text editors / github.

long strings are reflowed when using :formatted and the REXML parser

The rexml pretty formatter will break up and indent long strings, changing the actual content of the string.

This means when reading the generated plist, you don't get the same data out.

With a 30-character string:

$ ruby -rcfpropertylist -e 'p = CFPropertyList::List.new; p.value = CFPropertyList.guess("foo" * 10); puts p.to_str(CFPropertyList::List::FORMAT_XML, :formatted => true)'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <string>foofoofoofoofoofoofoofoofoofoo</string>
</plist>

With a 90-character string:

$ ruby -rcfpropertylist -e 'p = CFPropertyList::List.new; p.value = CFPropertyList.guess("foo" * 30); puts p.to_str(CFPropertyList::List::FORMAT_XML, :formatted => true)'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
  <string>
    foofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoofoo
  </string>
</plist>

In the 90-character example, the string value has been changed, and now starts with a newline and two spaces.

The rexml pretty formatter has a width attribute:
http://ruby-doc.org/stdlib-2.1.0/libdoc/rexml/rdoc/REXML/Formatters/Pretty.html

Adding: f.width = 9999 following https://github.com/ckruse/CFPropertyList/blob/master/lib/cfpropertylist/rbREXMLParser.rb#L42 works around this problem.

nokogiri's formatter doesn't do this. I did not test libxml.

standardize plist data

Hi ckruse,
As you may have noticed, I am writing yet another plist library for Ruby, its called Plist4r. The aim of Plist4r is to provide a layer of features on top such as a flexible plugin architecture, easy-to-use interface, and so on.

More information about the Plist4r Gem is available here: http://dreamcat4.github.com/plist4r

As one of the plugins of Plist4r, your CFPropertyList Ruby Library is the best for Linux platforms. And Kevin Ballard's is the best for Mac OSX.

See: http://dreamcat4.github.com/plist4r/frames.html?http://dreamcat4.github.com/plist4r/file.Backends.html

Question:

We are left with 1 tricky problem which is to standardize the Ruby representation object for plist <data></data>. It can be implemented in many different ways. Here are 2 obvious choices:

  1. Use String class for representing both plist strings and plist data. Add blob? and blob=<true,false> to the String class. If string.blob? == true then its a binary string (plist Data), if string.blob? == false then its a regular string.

  2. Use StringIO as the standard Ruby class for data. StringIO of course has all the seek() and pos methods for manipulating a byte stream.

I am happy to go with either option, as long as we can standardize the use of these classes across these different ruby plist libraries.

There are positives and negatives for each one:

String.blob?
  Positives:
    - easier to implement, and very simple code that cant go wrong
    - calling inspect() on the string shows the raw binary data
    - easy to cache - the object is a static object
  Negatives:
    - Adding blob?() and blob=() to String class is global
    - If we inspect an empty data string, it looks the same as a normal string

StringIO
  Positives:
    - If you inspect() a StringIO object, it looks like this - "#<StringIO:0x13572c0>"
    - it gives the user byte stream operations and read(), puts() etc
  Negatives:
    - more difficult to implement
    - If you inspect() a StringIO object, you do not see the raw binary data
    - its a complex object, with many states (so it does not cache easily)
    - we must rewind() it each time
    - (in plist4r) we must override eql?(), ==(), hash(), and other methods

So what do you think?

Crash if something is nil

require "rbcfpropertylist"
plist = CFPropertyList::List.new
data = { :bar => 1, :foo => nil }
plist.value = CFPropertyList.guess data
plist.to_str
# => NoMethodError: undefined method `to_binary' for nil:NilClass

Format error! (CFFormatError) on nil key when using REXML parser

When CFPropertyList falls back to using REXML for parsing and it encounters a nil key, it raises. This is east to reproduce using the file referenced below.

#!/usr/bin/ruby

require 'rubygems'
require 'cfpropertylist'

file = '/System/Library/Security/authorization.plist'

plist = CFPropertyList::List.new(:file => file)
# CFPropertyList.native_types(plist.value)

Results:

/Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbREXMLParser.rb:101:in `block in import_xml': Format error! (CFFormatError)
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/element.rb:905:in `block in each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/xpath.rb:67:in `each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/xpath.rb:67:in `each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/element.rb:905:in `each'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbREXMLParser.rb:93:in `import_xml'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbREXMLParser.rb:102:in `block in import_xml'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/element.rb:905:in `block in each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/xpath.rb:67:in `each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/xpath.rb:67:in `each'
    from /System/Library/Frameworks/Ruby.framework/Versions/2.0/usr/lib/ruby/2.0.0/rexml/element.rb:905:in `each'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbREXMLParser.rb:93:in `import_xml'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbREXMLParser.rb:23:in `load'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbCFPropertyList.rb:347:in `load'
    from /Library/Ruby/Gems/2.0.0/gems/CFPropertyList-2.2.5/lib/rbCFPropertyList.rb:247:in `initialize'
    from /Users/bcw/Desktop/foo.rb:8:in `new'
    from /Users/bcw/Desktop/foo.rb:8:in `<main>'

BigEndian platform support

Trying to run the test suite of CFProperty list on PPC64, which is BigEndian platfor, fails with following errors:

+ ruby -Ilib:test -e 'Dir.glob "./test/**/test_*.rb", &method(:require)'
Run options: --seed 51868
# Running:
FFF..FF.FFF...................................................................................
Finished in 13.451500s, 6.9881 runs/s, 14917.2949 assertions/s.
  1) Failure:
TestReal#test_read_float [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_real.rb:14]:
Expected: 1.5
  Actual: 6.896490392174587e-41
  2) Failure:
TestReal#test_write_double [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_real.rb:26]:
--- expected
+++ actual
@@ -1 +1 @@
-"bplist00#?\xF8\x00\x00\x00\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
+"bplist00#\x00\x00\x00\x00\x00\x00\xF8?\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
  3) Failure:
TestReal#test_read_double [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_real.rb:19]:
Expected: 1.5
  Actual: 3.13984e-319
  4) Failure:
TestDate#test_write_date_1900 [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_date.rb:32]:
--- expected
+++ actual
@@ -1 +1 @@
-"bplist003\xC1\xE7\xBF3\xC8\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
+"bplist003\x00\x00\x00\xC83\xBF\xE7\xC1\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
  5) Failure:
TestDate#test_write_date_from_date_object [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_date.rb:39]:
--- expected
+++ actual
@@ -1 +1 @@
-"bplist003A\xB3{Z\x80\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
+"bplist003\x00\x00\x00\x80Z{\xB3A\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
  6) Failure:
TestDate#test_write_date_epoch [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_date.rb:25]:
--- expected
+++ actual
@@ -1 +1 @@
-"bplist003\xC1\xCD'\xE4@\x00\x00\x00\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
+"bplist003\x00\x00\x00@\xE4'\xCD\xC1\b\x00\x00\x00\x00\x00\x00\x01\x01\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11"
  7) Failure:
TestDate#test_read_date_1900 [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_date.rb:18]:
Expected: 1900-01-01 12:00:00 UTC
  Actual: 2001-01-01 00:00:00 +0000
  8) Failure:
TestDate#test_read_date_epoch [/builddir/build/BUILD/CFPropertyList-2.3.3/usr/share/gems/gems/CFPropertyList-2.3.3/test/test_date.rb:13]:
Expected: 1970-01-01 00:00:00 +0000
  Actual: 2001-01-01 00:00:00 +0000
94 runs, 200660 assertions, 8 failures, 0 errors, 0 skips

CFPropertyList gem

What do you think of the idea to provide the CFPropertaList as a easy to install gem?

XML Parser cant load empty Plist

Hi,

For an xml string like <plist><dict></dict></plist>, it seems that CFPropertyList cannot handle.

We get an exception:

#<Plist4r::Backend::CFPropertyList::CFFormatError: Format error!>
    from ./lib/plist4r/backend/c_f_property_list/rbXMLCFPropertyList.rb:79:in `import_xml'
    from ./lib/plist4r/backend/c_f_property_list/rbXMLCFPropertyList.rb:73:in `each'
    from ./lib/plist4r/backend/c_f_property_list/rbXMLCFPropertyList.rb:73:in `import_xml'
    from ./lib/plist4r/backend/c_f_property_list/rbXMLCFPropertyList.rb:24:in `load'
    from ./lib/plist4r/backend/c_f_property_list/rbCFPropertyList.rb:227:in `load_str'
    from /Users/id/tmp/plist4r/lib/plist4r/backend/c_f_property_list.rb:12:in `from_string'
    from ./lib/plist4r/backend.rb:79:in `send'
    from ./lib/plist4r/backend.rb:79:in `call'
    from ./lib/plist4r/backend.rb:71:in `each'
    from ./lib/plist4r/backend.rb:71:in `call'
    from ./lib/plist4r/plist_cache.rb:27:in `from_string'
    from ./lib/plist4r/plist.rb:78:in `from_string'
    from (eval):1:in `parse_opts'
    from ./lib/plist4r/plist.rb:273:in `eval'
    from ./lib/plist4r/plist.rb:276:in `parse_opts'
    from ./lib/plist4r/plist.rb:273:in `each'
    from ./lib/plist4r/plist.rb:273:in `parse_opts'
    from ./lib/plist4r/plist.rb:51:in `initialize'
    from ./lib/plist4r.rb:23:in `new'
    from ./lib/plist4r.rb:23:in `new'
    from ./lib/plist4r/backend/test/harness.rb:124:in `run_tests'
    from ./lib/plist4r/mixin/ordered_hash.rb:125:in `each_pair'
    from ./lib/plist4r/mixin/ordered_hash.rb:125:in `each'
    from ./lib/plist4r/mixin/ordered_hash.rb:125:in `each_pair'
    from ./lib/plist4r/backend/test/harness.rb:120:in `run_tests'
    from ./lib/plist4r/backend/test/harness.rb:116:in `each'
    from ./lib/plist4r/backend/test/harness.rb:116:in `run_tests'
    from test.rb:44:in `harness'
    from test.rb:46

Not sure if this affects the binary parser also.

uninitialized constant: StringIO

I am seeing: this error
/Library/Ruby/Gems/1.8/gems/CFPropertyList-2.2.1/lib/rbBinaryCFPropertyList.rb:23:in `load': uninitialized constant CFPropertyList::Binary::StringIO (NameError)

And after searching the CFPropertyList code it looks like this library is never required. I added:

require 'stringio'

To my own code that fixed it.

Strings longer than 14 characters cannot be serialized

plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess({'name' => 'abcdefghijklmno'})
plist.save("example.plist", CFPropertyList::List::FORMAT_BINARY)

NoMethodError: undefined method `pack' for "\x10":String 
from ../CFPropertyList/rbBinaryCFPropertyList.rb:402:in `int_bytes'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:416:in `type_bytes'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:534:in `string_to_binary'
from ../CFPropertyList/rbCFTypes.rb:46:in `to_binary'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:660:in `block in dict_to_binary'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:659:in `each_value'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:659:in `dict_to_binary'
from ../CFPropertyList/rbCFTypes.rb:228:in `to_binary'
from ../CFPropertyList/rbBinaryCFPropertyList.rb:93:in `to_str'
from ../CFPropertyList/rbCFPropertyList.rb:269:in `save'
from (irb):72
from /usr/local/bin/irb:12:in `<main>'

Symbols in arrays

Symbols are handled as String when used as Dictionary keys, but not in an Array. Is there any specific reason that CFPropertyList does not support it?

I'm converting a hash to an array of arrays because Dictionary type in Obj-C / Swift is not ordered. I believe it would be a typical use case to do something like {small: 'Small', large: 'Large'}.to_a.

data = [["Large", :large],["Small", :small]]
plist = CFPropertyList::List.new
plist.value = CFPropertyList.guess(data)

CFTypeError: Unknown class Symbol. Try using :convert_unknown_to_string if you want to use unknown object types!

Plain text plists cannot be handled by the gem

The following plist:

{ Filter = { Bundles = ( "com.facebook.Messenger" ); }; }

Leads to the error:

CFFormatError: invalid XML: Start tag expected, '<' not found
    from /Users/egjoka/.rvm/gems/ruby-2.1.5/gems/CFPropertyList-2.2.8/lib/cfpropertylist/rbNokogiriParser.rb:25:in `rescue in load'
    from /Users/egjoka/.rvm/gems/ruby-2.1.5/gems/CFPropertyList-2.2.8/lib/cfpropertylist/rbNokogiriParser.rb:13:in `load'
    from /Users/egjoka/.rvm/gems/ruby-2.1.5/gems/CFPropertyList-2.2.8/lib/cfpropertylist/rbCFPropertyList.rb:348:in `load'
    from /Users/egjoka/.rvm/gems/ruby-2.1.5/gems/CFPropertyList-2.2.8/lib/cfpropertylist/rbCFPropertyList.rb:248:in `initialize'
    from (irb):2:in `new'
    from (irb):2
    from /Users/egjoka/.rvm/rubies/ruby-2.1.5/bin/irb:11:in `<main>'

Any specific reason to use NOENT?

@ckruse Hi Christian, found NOENT in both rbLibXMLParser.rb and rbNokogiriParser.rb
Passing NOENT as parsing option is risky.

Passing NOENT (which is used to substitute entities) as parsing options permits processing of entities, including both regular and external. That means NONET and NODTDLOAD will be of no use if NOENT is there.

Take a look at this:
sparklemotion/nokogiri#1582 (comment)

This is why Nokogiri team strictly suggests using default parsing options:
DEFAULT_XML = RECOVER | NONET

NOENT

Dictionaries are nil

Given the following plst:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>foo</key>
    <array>
        <dict>
            <key>bar</key>
            <dict>
                <key>baz</key>
                <integer>42</integer>
            </dict>
        <dict>
    </array>
</dict>
</plist>

and this ruby file:

require 'cfpropertylist'

filepath = ARGV[0]
plist = CFPropertyList::List.new(:file => filepath)
data = CFPropertyList.native_types(plist.value)

data['foo'].each do |item|
  puts item['bar'] if object.is_a? Hash
end

I get the following output:

{"bar"=>nil}

when running:

ruby test.rb path/to/file.plist

Defining Enumerable::Enumerator breaks AWS S3 Ruby client

A short program like this:

#!/usr/bin/ruby
require 'aws-sdk-s3'
require 'cfpropertylist'

bucket = Aws::S3::Resource.new().bucket('any-bucket-name')
bucket_objs = { }
bucket.objects.each { |obj| printf "%s\n",obj.key }

fails with

/Library/Ruby/Gems/2.3.0/gems/aws-sdk-core-3.13.1/lib/aws-sdk-core/resources/collection.rb:56:in each': undefined method each' for #<Enumerable::Enumerator:0x007f80e8f0d758> (NoMethodError)

with ruby 2.3.3p222, CFPropertyList 2.3.6, and aws-sdk-core 3.13.1 (and other stuff, but those are the relevant pieces).

The problem is that cfpropertylist has defined, in lib/cfpropertylist/rbCFPropertyList.rb,

module Enumerable
  class Enumerator
  end
end

in addition to the usual Ruby 2.3 top-level Enumerator. Then the AWS SDK does

module Aws
  module Resources
    class Collection

      extend Aws::Deprecations
      include Enumerable
...
      def each(&block)
        enum = Enumerator.new do |y|
...

and instead of the usual Enumerator class, it's getting the Enumerable::Enumerator that cfpropertylist has defined, which doesn't have any useful code, and in particular has no 'each' method.

I worked around this by writing

require 'aws-sdk-s3'
require 'cfpropertylist'

module Enumerable
       remove_const(:Enumerator)
end

but really I think CFPropertyList shouldn't be defining things in system modules.

Missing tag

Could you please push tag for 2.3.0? Thx.

Please include LICENSE file in gem

Although your repository contains LICENSE file, this file is not shipped in gem. Could you please add the file into the gem? Thank you.

Error around the Enumerable::Enumerator class

This may just be an error in my implementation, but I thought I'd paste it here to start the conversation.

I'm trying to require the CFPropertyList gem inside a provider that I'm using for Puppet. To do a 'safety check' for this, I created a Puppet 'feature' that will basically try to load the library and safely error-out if the lib isn't found.

The feature looks like so:

require 'puppet/util/feature'
Puppet.features.add(:cfpropertylist, :libs => ['CFPropertyList'])

In my provider code, I require the feature with:

confine :feature => :cfpropertylist

Cool, so when I try to run Puppet it complains about the feature not being available because the CFPropertyList library failed to load. Yikes. Okay, so I commented all the feature code out and just tried to require CFPropertyList at the top of my provider to see if I could expose more debugging messages. This bubbles up this stacktrace:

Notice: Compiled catalog for satori.local in environment production in 0.02 seconds
Error: Could not autoload puppet/feature/cfpropertylist: wrong number of arguments(1 for 0)
/Users/glarizza/src/puppet-property_list_key/vendor/ruby/1.9.1/gems/CFPropertyList-2.2.4/lib/rbCFPropertyList.rb:91:in `initialize'
/Users/glarizza/src/puppet-property_list_key/vendor/ruby/1.9.1/gems/CFPropertyList-2.2.4/lib/rbCFPropertyList.rb:91:in `new'
/Users/glarizza/src/puppet-property_list_key/vendor/ruby/1.9.1/gems/CFPropertyList-2.2.4/lib/rbCFPropertyList.rb:91:in `<top (required)>'
/Users/glarizza/src/puppet-property_list_key/vendor/ruby/1.9.1/gems/CFPropertyList-2.2.4/lib/CFPropertyList.rb:3:in `require'
/Users/glarizza/src/puppet-property_list_key/vendor/ruby/1.9.1/gems/CFPropertyList-2.2.4/lib/CFPropertyList.rb:3:in `<top (required)>'
/Users/glarizza/src/puppet-property_list_key/lib/puppet/feature/cfpropertylist.rb:2:in `require'
/Users/glarizza/src/puppet-property_list_key/lib/puppet/feature/cfpropertylist.rb:2:in `<top (required)>'

That's pointing back to the Enumerator::Enumerable hack that you used for the 1.8/1.9 codebase differences. So I jumped into IRB:

rirb(main):001:0> require 'puppet'
re=> true
irb(main):002:0> require 'CFPropertyList'
=> true
irb(main):015:0* begin
irb(main):016:1*   Enumerable::Enumerator.new([])
irb(main):017:1> rescue NameError => e
irb(main):018:1>   module Enumerable
irb(main):019:2>     class Enumerator
irb(main):020:3>     end
irb(main):021:2>   end
irb(main):022:1> end
ArgumentError: wrong number of arguments(1 for 0)
        from (irb):16:in `initialize'
        from (irb):16:in `new'
        from (irb):16
        from /opt/boxen/rbenv/versions/1.9.3-p392/bin/irb:12:in `<main>'

I then tried a brief hack that we'd talked about awhile back:

rirb(main):001:0> require 'puppet'
re=> true
irb(main):002:0> require 'CFPropertyList'
=> true
irb(main):003:0> begin
irb(main):004:1*   Enumerable::Enumerator.new([])
irb(main):005:1> rescue NameError => e
irb(main):006:1>   module Enumerable
irb(main):007:2>     class Enumerator
irb(main):008:3>       def initialize(*args)
irb(main):009:4>       end
irb(main):010:3>     end
irb(main):011:2>   end
irb(main):012:1> end
ArgumentError: wrong number of arguments(1 for 0)
        from (irb):4:in `initialize'
        from (irb):4:in `new'
        from (irb):4
        from /opt/boxen/rbenv/versions/1.9.3-p392/bin/irb:12:in `<main>'
irb(main):013:0>

I'm kinda stumped at the moment - it requires just fine in IRB but when I try to require the library inside the Puppet codebase I'm getting errors. I'm guessing maybe Puppet is doing some work with the Enumerable::Enumerator class, so I really need to track that down. Do you have any suggestions?

How do you save a binary file after making changes to it.

I need to edit couple of properties in the plist

plist = CFPropertyList::Binary.new.load(:file =>plist_file)
plist.value["prop1"]=true
plist.value["prop2"]=true

But after this I am not able to figure out how to save this. Should I create a new CFProperty list and then use it to save?

Cannot parse some sort of NeXT Step style Property List

CFPropertyList::List raises an error when the following types of Property List.

1. Dictionary without surrounding { }

require 'cfpropertylist'

data = 'foo = bar;'
CFPropertyList::List.new(data: data)
/path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbPlainCFPropertyList.rb:25:in `load': content after root object (CFFormatError)
	from /path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbCFPropertyList.rb:321:in `load_str'
	from /path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbCFPropertyList.rb:239:in `initialize'
	from /var/folders/tm/wy8sj46s6yq4rlfgsxyvd1l80000gn/T/vdeL7YX/205:5:in `new'
	from /var/folders/tm/wy8sj46s6yq4rlfgsxyvd1l80000gn/T/vdeL7YX/205:5:in `<main>'

I cannot find the specification but I think it is a valid format because of some reasons.

  1. plutil can parse it.
$ echo 'foo = bar;' | plutil -p -
{
  "foo" => "bar"
}
  1. pl can parse it.
$ echo 'foo = bar;' | pl
{
    foo = bar;
}
  1. Foundation classes can parse it.
import Foundation

try! "foo = bar;".write(toFile: "foobar.plist", atomically: false, encoding: .utf8)
print(NSDictionary(contentsOfFile: "foobar.plist")!)
// prints
// {
//     foo = bar;
// }

And also, .strings format, which is often used as iOS localized string file, is encoded as this kind of Property List.

Whitespaces at the end

require 'cfpropertylist'

data = "foo\n"
CFPropertyList::List.new(data: data)
/path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbPlainCFPropertyList.rb:25:in `load': content after root object (CFFormatError)
	from /path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbCFPropertyList.rb:321:in `load_str'
	from /path/to/CFPropertyList-3.0.7/lib/cfpropertylist/rbCFPropertyList.rb:239:in `initialize'
	from /var/folders/tm/wy8sj46s6yq4rlfgsxyvd1l80000gn/T/vdeL7YX/188:5:in `new'
	from /var/folders/tm/wy8sj46s6yq4rlfgsxyvd1l80000gn/T/vdeL7YX/188:5:in `<main>'

I think it is also valid because of the following reasons.

  1. plutil can parse it.
$ printf 'foo\n' | plutil -p -
"foo"
  1. pl can parse it.
$ printf 'foo\n' | pl
foo
  1. Foundation classes can parse it.
import Foundation

try! "foo\n".write(toFile: "foobar.plist", atomically: false, encoding: .utf8)
print(try! NSString(contentsOfFile: "foobar.plist", encoding: String.Encoding.utf8.rawValue))
// prints foo

Add `nkf` to gemspec

After upgrading to Ruby 3.3.0 running bundle install for a single Gemfile which only contains Fastlane gem shows this warning

{PROJECT_ROOT}/vendor/bundle/ruby/3.3.0/gems/CFPropertyList-3.0.6/lib/cfpropertylist/rbCFPropertyList.rb:3: warning: kconv is found in nkf, which will no longer be part of the default gems since Ruby 3.4.0. Add nkf to your Gemfile or gemspec. Also contact author of CFPropertyList-3.0.6 to add nkf into its gemspec.

Mention Monkey Patches in Readme

It would be very helpful to mention the #to_plist monkey patches in the readme so that people can see other ways that this can get used.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.