plist4r 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|