plist4r 0.2.1 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +2 -0
- data/.nojekyll +0 -0
- data/.yardopts +1 -0
- data/README.rdoc +18 -23
- data/Rakefile +16 -7
- data/VERSION +1 -1
- data/bin/plist4r +8 -0
- data/lib/plist4r.rb +35 -7
- data/lib/plist4r/application.rb +19 -0
- data/lib/plist4r/backend.rb +23 -2
- data/lib/plist4r/backend/example.rb +45 -4
- data/lib/plist4r/backend/haml.rb +1 -0
- data/lib/plist4r/backend/libxml4r.rb +3 -2
- data/lib/plist4r/backend/plutil.rb +4 -1
- data/lib/plist4r/backend/ruby_cocoa.rb +10 -3
- data/lib/plist4r/commands.rb +61 -0
- data/lib/plist4r/config.rb +21 -1
- data/lib/plist4r/mixin/data_methods.rb +23 -3
- data/lib/plist4r/mixin/mixlib_cli.rb +199 -0
- data/lib/plist4r/mixin/mixlib_config.rb +158 -152
- data/lib/plist4r/mixin/ordered_hash.rb +143 -135
- data/lib/plist4r/mixin/popen4.rb +25 -5
- data/lib/plist4r/mixin/ruby_stdlib.rb +13 -1
- data/lib/plist4r/options.rb +44 -0
- data/lib/plist4r/plist.rb +156 -13
- data/lib/plist4r/plist_type.rb +25 -4
- data/lib/plist4r/plist_type/launchd.rb +377 -263
- data/plist4r.gemspec +56 -48
- data/test.rb +2 -2
- metadata +13 -7
- data/lib/plist4r/mixin/class_attributes.rb +0 -128
data/.gitignore
CHANGED
data/.nojekyll
ADDED
File without changes
|
data/.yardopts
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--no-private
|
data/README.rdoc
CHANGED
@@ -1,12 +1,10 @@
|
|
1
1
|
= plist4r
|
2
2
|
|
3
|
-
|
3
|
+
Plist4r is a friendly ruby library for reading and writing plist files.
|
4
4
|
|
5
|
-
Current status:
|
5
|
+
==== Current status: Beta, 0.2.x series
|
6
6
|
|
7
|
-
We can read / write
|
8
|
-
|
9
|
-
Future `Stable` will be targeted, 0.3.x series.
|
7
|
+
We can read / write various kinds of plist files reliably. The API interfaces (for the pluggable backends and plist_types) are mature. The user API works well. Searchable Documentation is included, complete with examples.
|
10
8
|
|
11
9
|
== Installation
|
12
10
|
|
@@ -36,20 +34,6 @@ Future `Stable` will be targeted, 0.3.x series.
|
|
36
34
|
|
37
35
|
p.save
|
38
36
|
|
39
|
-
== Plist 'Types'
|
40
|
-
|
41
|
-
A Plist type can be one of `%w[info launchd]`, and is the data type for the whole plist file. A plist data type can provide convenience methods to set Type-specific plist structures. For example "Sockets" in a launchd plist.
|
42
|
-
|
43
|
-
Plist types are also useful to disallow keys which arent recognized or supported by that format. Flicking `:unsupported_keys` the Plist4r config will enable this:
|
44
|
-
|
45
|
-
::Plist4r::Config[:unsupported_keys] = false
|
46
|
-
|
47
|
-
Or individually, per plist object with
|
48
|
-
|
49
|
-
plist.unsupported_keys false
|
50
|
-
|
51
|
-
Default is true, which allows editing of any plist keys. We think thats a good choice, since unsupported keys can already be present in existing plist files, which are loadable by Plist4r.
|
52
|
-
|
53
37
|
== Plist4r Backends
|
54
38
|
|
55
39
|
There are now a number of ruby libraries which can read / write plist files. The aim of plist4r is to utilize the individual best features from all of those libraries, as a series of "backends". And hide those behind a "frontend" that is easy to work with.
|
@@ -66,6 +50,20 @@ And (as above) the 3 supported Plist file formats are
|
|
66
50
|
|
67
51
|
We believe thats allright for most uses, and decided to include `next_step` for completeness. `NextStep` is also known by other names such as `OpenStep` and (more updated version) `GNU Step`. For example the apple `defaults` command on Mac OS-X will still return `NextStep` formatted plist data.
|
68
52
|
|
53
|
+
== Plist4r Types
|
54
|
+
|
55
|
+
A Plist type can be one of `%w[plist info launchd]`, and is the data type for the whole plist file. The plist data type provides convenience methods for setting the Type-specific data structures. For example "Sockets" in a launchd plist.
|
56
|
+
|
57
|
+
Plist types are also useful to disallow keys which arent recognized or supported by that format. Setting `:strict_keys true` the Plist4r::Config object will globaly enable strict keys.
|
58
|
+
|
59
|
+
::Plist4r::Config[:unsupported_keys] = true
|
60
|
+
|
61
|
+
Or individually, per plist object with
|
62
|
+
|
63
|
+
plist.strict_keys false
|
64
|
+
|
65
|
+
Default is false, which allows editing of any arbitrary plist keys. We think thats a good choice, since unsupported keys can already be present in many existing plist files.
|
66
|
+
|
69
67
|
== More Examples
|
70
68
|
|
71
69
|
module ::Plist4r::Backend::MyPlistReaderWriter
|
@@ -116,11 +114,8 @@ Plist4r has now moved from alpha to beta - quality software. TBC...
|
|
116
114
|
* Regression Tests (rspec)
|
117
115
|
* Test harness for the backends
|
118
116
|
* Testing of the individual backends
|
119
|
-
* Tests for Plist Types
|
120
|
-
* RDoc Documentation
|
121
|
-
* Script for embedding / inlining Plist4r into Homebrew
|
122
117
|
* A Plist Type for Info.plist
|
123
|
-
*
|
118
|
+
* Tests for Plist Types
|
124
119
|
|
125
120
|
== Notes on Patches/Pull Requests
|
126
121
|
|
data/Rakefile
CHANGED
@@ -47,11 +47,20 @@ end
|
|
47
47
|
|
48
48
|
task :default => :spec
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
rescue LoadError
|
54
|
-
task :yardoc do
|
55
|
-
abort "YARD is not available. In order to run yardoc, you must: sudo gem install yard"
|
56
|
-
end
|
50
|
+
require 'yard'
|
51
|
+
YARD::Rake::YardocTask.new do |t|
|
52
|
+
t.after = lambda { `touch doc/.nojekyll` }
|
57
53
|
end
|
54
|
+
|
55
|
+
Jeweler::GhpagesTasks.new do |ghpages|
|
56
|
+
ghpages.push_on_release = true
|
57
|
+
ghpages.set_repo_homepage = true
|
58
|
+
ghpages.user_github_com = false
|
59
|
+
ghpages.doc_task = "yard"
|
60
|
+
ghpages.keep_files = []
|
61
|
+
ghpages.map_paths = {
|
62
|
+
".nojekyll" => "",
|
63
|
+
"doc" => "",
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.2.
|
1
|
+
0.2.2
|
data/bin/plist4r
ADDED
data/lib/plist4r.rb
CHANGED
@@ -4,26 +4,44 @@ $LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
|
4
4
|
|
5
5
|
require 'plist4r/plist'
|
6
6
|
|
7
|
+
# Almost everything required by Plist4r is fully encapsulated within the {Plist4r} namespace.
|
8
|
+
# However there are a few exceptions. We had to add a couple methods to {Object} and {String}.
|
7
9
|
module Plist4r
|
8
10
|
class << self
|
11
|
+
|
12
|
+
# Calls Plist4r::Plist.new with the supplied arguments and block
|
13
|
+
#
|
14
|
+
# @return [Plist4r::Plist] The new Plist object
|
15
|
+
# @see Plist4r::Plist#initialize
|
16
|
+
# @example Create new, empty plist
|
17
|
+
# Plist4r.new => #<Plist4r::Plist:0x111546c @file_format=nil, ...>
|
9
18
|
def new *args, &blk
|
10
19
|
# puts args.inspect
|
11
20
|
return Plist.new *args, &blk
|
12
21
|
end
|
13
22
|
|
23
|
+
# Opens a plist file
|
24
|
+
#
|
25
|
+
# @param [String] filename plist file to load
|
26
|
+
# @return [Plist4r::Plist] The loaded Plist object
|
27
|
+
# @example Load from file
|
28
|
+
# Plist4r.open("example.plist") => #<Plist4r::Plist:0x1152d1c @file_format="xml", ...>
|
29
|
+
# @see Plist4r::Plist#initialize
|
30
|
+
# @see Plist4r::Plist#open
|
14
31
|
def open filename, *args, &blk
|
15
|
-
# puts args.inspect
|
16
32
|
p = Plist.new filename, *args, &blk
|
17
33
|
p.open
|
18
34
|
end
|
19
|
-
|
35
|
+
|
36
|
+
# Given an string of Plist data, peek the first few bytes and detect the file format
|
37
|
+
#
|
38
|
+
# @param [String] string of plist data
|
39
|
+
# @return [Symbol] A Symbol representing the plist data type. One of: Plist4r::Plist.FileFormats
|
40
|
+
# @see Plist4r::Plist.FileFormats
|
41
|
+
# @example
|
42
|
+
# Plist4r.string_detect_format("{ \"key1\" = \"value1\"; \"key2\" = \"value2\"; }") => :next_step
|
20
43
|
def string_detect_format string
|
21
|
-
# puts "in string_detect_format"
|
22
|
-
# puts "string = #{string.inspect}"
|
23
|
-
# s = string.strip
|
24
44
|
string.strip! if string[0,1] =~ /\s/
|
25
|
-
# s = string
|
26
|
-
# puts "s = #{s.inspect}"
|
27
45
|
case string[0,1]
|
28
46
|
when "{","("
|
29
47
|
:next_step
|
@@ -44,6 +62,11 @@ module Plist4r
|
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
65
|
+
# Given a Plist filename, peek the first few bytes and detect the file format
|
66
|
+
#
|
67
|
+
# @param [String] filename plist file to check
|
68
|
+
# @see Plist4r.string_detect_format
|
69
|
+
# @see Plist4r::Plist.FileFormats
|
47
70
|
def file_detect_format filename
|
48
71
|
string_detect_format File.read(filename)
|
49
72
|
end
|
@@ -51,6 +74,11 @@ module Plist4r
|
|
51
74
|
end
|
52
75
|
|
53
76
|
class String
|
77
|
+
|
78
|
+
# Converts a string of plist data into a new Plist4r::Plist object
|
79
|
+
#
|
80
|
+
# @return [Plist4r::Plist] The new Plist object
|
81
|
+
# @see Plist4r::Plist#initialize
|
54
82
|
def to_plist
|
55
83
|
return ::Plist4r.new(:from_string => self)
|
56
84
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'plist4r/config'
|
2
|
+
require 'plist4r/options'
|
3
|
+
require 'plist4r/commands'
|
4
|
+
|
5
|
+
module Plist4r
|
6
|
+
# The Plist4r Application Object. Instantiated for command-line mode
|
7
|
+
# @see Plist4r::CLI
|
8
|
+
class Application
|
9
|
+
|
10
|
+
def initialize *args, &blk
|
11
|
+
@cli = Plist4r::CLI.new
|
12
|
+
Plist4r::Config[:args] = @cli.parse
|
13
|
+
|
14
|
+
puts "Plist4r::Config[:args] = " + Plist4r::Config[:args].inspect
|
15
|
+
@commands = Plist4r::Commands.new
|
16
|
+
@commands.run
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/plist4r/backend.rb
CHANGED
@@ -4,9 +4,17 @@ require 'plist4r/backend_base'
|
|
4
4
|
require 'plist4r/mixin/ordered_hash'
|
5
5
|
|
6
6
|
module Plist4r
|
7
|
+
# This class is the Backend broker. The purpose of this object is to manage and handle API
|
8
|
+
# calls, passing them over to the appropriate Plist4r backends.
|
7
9
|
class Backend
|
10
|
+
# The list of known backend API methods. Any combination or subset of API methods
|
11
|
+
# can be implemented by an individual backend.
|
12
|
+
# @see Plist4r::Backend::Example
|
8
13
|
ApiMethods = %w[from_string to_xml to_binary to_next_step open save]
|
9
|
-
|
14
|
+
|
15
|
+
# A new instance of Backend. A single Backend will exist for the the life of
|
16
|
+
# the Plist object. The attribute @plist is set during initialization and
|
17
|
+
# refers back to the plist instance object.
|
10
18
|
def initialize plist, *args, &blk
|
11
19
|
@plist = plist
|
12
20
|
@backends = plist.backends.collect do |b|
|
@@ -22,7 +30,20 @@ module Plist4r
|
|
22
30
|
end
|
23
31
|
|
24
32
|
# vv We need a version of this to call our matrix test harness vv
|
25
|
-
|
33
|
+
# Call a Plist4r API Method. Here, we usually pass a {Plist4r::Plist} object
|
34
|
+
# as one of the parameters, which will also contain all the input data to work on.
|
35
|
+
#
|
36
|
+
# This function loops through the array of available backends, and calls the
|
37
|
+
# same method on the first backend found to implemente the specific request.
|
38
|
+
#
|
39
|
+
# If the request fails, the call is re-executed on the next available
|
40
|
+
# backend.
|
41
|
+
#
|
42
|
+
# The plist object is updated in-place.
|
43
|
+
#
|
44
|
+
# @raise if no backend is able to sucessfully execute the request.
|
45
|
+
# @param [Symbol] method_sym The API method call to execute
|
46
|
+
# @param *args Any optional arguments to pass to the backend
|
26
47
|
def call method_sym, *args, &blk
|
27
48
|
# puts "in call"
|
28
49
|
# puts "#{method_sym.inspect} #{args.inspect}"
|
@@ -1,39 +1,77 @@
|
|
1
1
|
|
2
2
|
require 'plist4r/backend_base'
|
3
3
|
|
4
|
+
# An example Plist4r Backend.
|
5
|
+
# These examples demonstrate the common convenience methods which are available to your backend.
|
6
|
+
# Its not necessary to implement all of the API methods in your backend.
|
7
|
+
# You may selectively implement any single method or method(s).
|
8
|
+
#
|
9
|
+
# In the case of {from_string} and {open}, the source plist format is not known beforehand.
|
10
|
+
# For those cases you should call {Plist4r.string_detect_format} or {Plist4r.file_detect_format}
|
11
|
+
# as appropriate. Then raise an exception for those situation where backend is unable to continue.
|
12
|
+
#
|
13
|
+
# For example, if your backend is only able to load :xml files, then it should raise an exception
|
14
|
+
# whenever it encounters :binary or :next_step formatted files. This is intentional.
|
15
|
+
# By throwing the error, it means the API call can be picked up and passed on to the next available backend.
|
16
|
+
#
|
17
|
+
# @see Plist4r::Backend
|
4
18
|
module Plist4r::Backend::Example
|
5
19
|
class << self
|
20
|
+
|
21
|
+
# Parse a String of plist data and store it into the supplied {Plist4r::Plist}
|
22
|
+
# * Please click "View Source" for the example.
|
23
|
+
# @param [Plist4r::Plist] plist The plist object to read the string from
|
24
|
+
# @return [Plist4r::Plist] the same plist object, but updated to match its from_string
|
25
|
+
# @see Plist4r::Plist#from_string
|
26
|
+
# @see Plist4r.string_detect_format
|
6
27
|
def from_string plist
|
7
28
|
plist_string = plist.from_string
|
8
29
|
plist_format = Plist4r.string_detect_format plist.from_string
|
9
30
|
unless [:supported_fmt1,:supported_fmt2].include? plist_format
|
10
31
|
raise "#{self} - cant convert string of format #{plist_format}"
|
11
32
|
end
|
12
|
-
hash = ::
|
33
|
+
hash = ::Plist4r::OrderedHash.new
|
13
34
|
# import / convert plist data into ruby ordered hash
|
14
35
|
plist.import_hash hash
|
15
36
|
plist.file_format plist_format
|
16
37
|
return plist
|
17
38
|
end
|
18
39
|
|
40
|
+
# Convert a {Plist4r::Plist} into an "xml" formatted plist String
|
41
|
+
# * Please click "View Source" for the example.
|
42
|
+
# @param [Plist4r::Plist] plist The plist object to convert
|
43
|
+
# @return [String] the xml string
|
19
44
|
def to_xml plist
|
20
45
|
hash = plist.to_hash
|
21
46
|
xml_string = "Convert the plists's nested ruby hash into xml here"
|
22
47
|
return xml_string
|
23
48
|
end
|
24
49
|
|
25
|
-
|
50
|
+
# Convert a {Plist4r::Plist} into an "binary" formatted plist String
|
51
|
+
# * Please click "View Source" for the example.
|
52
|
+
# @param [Plist4r::Plist] plist The plist object to convert
|
53
|
+
# @return [String] the binary string
|
54
|
+
def to_binary plist
|
26
55
|
hash = plist.to_hash
|
27
56
|
binary_string = "Convert the plists's nested ruby hash into binary format here"
|
28
57
|
return binary_string
|
29
58
|
end
|
30
59
|
|
31
|
-
|
60
|
+
# Convert a {Plist4r::Plist} into a "next_step" formatted plist String
|
61
|
+
# * Please click "View Source" for the example.
|
62
|
+
# @param [Plist4r::Plist] plist The plist object to convert
|
63
|
+
# @return [String] the next_step string
|
64
|
+
def to_next_step plist
|
32
65
|
hash = plist.to_hash
|
33
66
|
next_step_string = "Convert the plists's nested ruby hash into next_step format here"
|
34
67
|
return next_step_string
|
35
68
|
end
|
36
69
|
|
70
|
+
# Open a plist file and store the contents into the supplied {Plist4r::Plist}
|
71
|
+
# * Please click "View Source" for the example.
|
72
|
+
# @param [Plist4r::Plist] plist The plist object to convert
|
73
|
+
# @return [String] the next_step string
|
74
|
+
# @see Plist4r.file_detect_format
|
37
75
|
def open plist
|
38
76
|
filename = plist.filename_path
|
39
77
|
file_format = Plist4r.file_detect_format filename
|
@@ -41,13 +79,16 @@ module Plist4r::Backend::Example
|
|
41
79
|
raise "#{self} - cant load file of format #{file_format}"
|
42
80
|
end
|
43
81
|
plist_file_as_string = File.read(filename)
|
44
|
-
hash = ::
|
82
|
+
hash = ::Plist4r::OrderedHash.new
|
45
83
|
# import / convert plist data into ruby ordered hash
|
46
84
|
plist.import_hash hash
|
47
85
|
plist.file_format file_format
|
48
86
|
return plist
|
49
87
|
end
|
50
88
|
|
89
|
+
# @param [Plist4r::Plist] plist The plist object to read the filename from
|
90
|
+
# @return [Plist4r::Plist] the same plist object, but updated to match the file contents
|
91
|
+
# @see Plist4r::Plist#filename_path
|
51
92
|
def save plist
|
52
93
|
filename = plist.filename_path
|
53
94
|
file_format = plist.file_format || Config[:default_format]
|
data/lib/plist4r/backend/haml.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
|
2
2
|
require 'plist4r/backend_base'
|
3
3
|
|
4
|
+
# Requires Libxml4r. Implements loading / parsing for the :xml file format only.
|
4
5
|
module Plist4r::Backend::Libxml4r
|
5
6
|
class << self
|
6
7
|
def tree_hash n
|
7
|
-
hash = ::
|
8
|
+
hash = ::Plist4r::OrderedHash.new
|
8
9
|
n_xml_keys = n.nodes["key"]
|
9
10
|
n_xml_keys.each do |n|
|
10
11
|
k = n.inner_xml
|
@@ -62,7 +63,7 @@ module Plist4r::Backend::Libxml4r
|
|
62
63
|
else
|
63
64
|
root = doc.node["/plist/array"]
|
64
65
|
if root
|
65
|
-
ordered_hash = ::
|
66
|
+
ordered_hash = ::Plist4r::OrderedHash.new
|
66
67
|
ordered_hash["Array"] = tree_array root
|
67
68
|
end
|
68
69
|
end
|
@@ -1,8 +1,11 @@
|
|
1
1
|
|
2
2
|
require 'plist4r/backend_base'
|
3
3
|
|
4
|
+
# This backend does not implement any official Plist4r API methods.
|
5
|
+
# But can be used to enhance and add functionality to other backends.
|
6
|
+
#
|
7
|
+
# (Mac OSX operating systems only)
|
4
8
|
module Plist4r::Backend::Plutil
|
5
|
-
# maybe this could be useful as a helper, used by other backends
|
6
9
|
class << self
|
7
10
|
def convert_file_to_xml
|
8
11
|
system "plutil -convert xml1 #{@filename}"
|
@@ -1,6 +1,13 @@
|
|
1
1
|
|
2
2
|
require 'plist4r/backend_base'
|
3
3
|
|
4
|
+
# This backend only works on MacOSX. It supports everything except {Backend::Example.to_next_step},
|
5
|
+
# and saving in the :next_step file format. Here we are calling the stock OSX Ruby in a seperate process.
|
6
|
+
# It isolates the runtime from any shared lib (.so) LoadErrors. And allows calling from other installed
|
7
|
+
# Ruby instances (eg REE), which dont usually have RubyCocoa enabled.
|
8
|
+
#
|
9
|
+
# This Backend should work for any 10.5 (Leopard), 10.6 (Snow Leopard) Mac OSX distribution.
|
10
|
+
# It will do nothing on non-mac platforms (eg Linux, etc).
|
4
11
|
module Plist4r::Backend::RubyCocoa
|
5
12
|
class << self
|
6
13
|
def ruby_cocoa_wrapper_rb
|
@@ -30,7 +37,7 @@ class OSX::NSObject
|
|
30
37
|
when OSX::NSArray
|
31
38
|
self.to_a.map { |x| x.is_a?(OSX::NSObject) ? x.to_ruby : x }
|
32
39
|
when OSX::NSDictionary
|
33
|
-
h = ::
|
40
|
+
h = ::Plist4r::OrderedHash.new
|
34
41
|
self.each do |x, y|
|
35
42
|
x = x.to_ruby if x.is_a?(OSX::NSObject)
|
36
43
|
y = y.to_ruby if y.is_a?(OSX::NSObject)
|
@@ -63,7 +70,7 @@ module Plist
|
|
63
70
|
else
|
64
71
|
plist_array = ::OSX::NSArray.arrayWithContentsOfFile(filename) unless plist_dict
|
65
72
|
raise "Couldnt parse file: #{filename}" unless plist_array
|
66
|
-
plist_dict = ::
|
73
|
+
plist_dict = ::Plist4r::OrderedHash.new
|
67
74
|
plist_dict["Array"] = plist_array.to_ruby
|
68
75
|
puts "#{plist_dict.inspect}"
|
69
76
|
end
|
@@ -159,7 +166,7 @@ EOC
|
|
159
166
|
result = ruby_cocoa_exec "open(\"#{filename}\")"
|
160
167
|
case result[1].exitstatus
|
161
168
|
when 0
|
162
|
-
hash = ::
|
169
|
+
hash = ::Plist4r::OrderedHash.new
|
163
170
|
eval("hash.replace("+result[2]+")")
|
164
171
|
plist.import_hash hash
|
165
172
|
else
|