plist4r 0.1.1 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +6 -2
- data/VERSION +1 -1
- data/lib/plist4r.rb +9 -4
- data/lib/plist4r/backend.rb +3 -3
- data/lib/plist4r/backend/example.rb +5 -4
- data/lib/plist4r/backend/plutil.rb +11 -10
- data/lib/plist4r/backend/ruby_cocoa.rb +40 -36
- data/lib/plist4r/config.rb +9 -6
- data/lib/plist4r/mixin/data_methods.rb +22 -4
- data/lib/plist4r/mixin/ordered_hash.rb +4 -3
- data/lib/plist4r/plist.rb +72 -39
- data/lib/plist4r/plist_cache.rb +14 -7
- data/lib/plist4r/plist_type.rb +37 -3
- data/lib/plist4r/plist_type/info.rb +6 -0
- data/lib/plist4r/plist_type/launchd.rb +2 -3
- data/lib/plist4r/plist_type/plist.rb +3 -0
- data/plist4r.gemspec +6 -7
- data/plists/{com.adobe.PDFAdminSettings.plist → example_big_binary.plist} +0 -0
- data/plists/{test.plist → example_medium_binary_launchd.plist} +0 -0
- data/plists/{foofoo.xml → example_medium_launchd.xml} +0 -0
- data/plists/{com.apple.SoftwareUpdate.plist → mini.xml} +0 -0
- data/test.rb +28 -3
- metadata +6 -7
- data/plists/Picture of Today.plist +0 -75
data/README.rdoc
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
Welcome to Plist4r, a ruby library for reading and writing plist files.
|
4
4
|
|
5
|
-
Current
|
5
|
+
Current status: `Beta`, 0.2.x series
|
6
|
+
|
7
|
+
We can read / write a `:launchd` plist. So thats pretty good. The API interfaces (for the pluggable backends and plist_types) are not going to change any more. The user API seems to work. If anyone would like to review the Plist4r code and give feedback / suggestions. Now is the time (whilst were still in beta).
|
8
|
+
|
9
|
+
Future `Stable` will be targeted, 0.3.x series.
|
6
10
|
|
7
11
|
== Installation
|
8
12
|
|
@@ -107,7 +111,7 @@ We believe thats allright for most uses, and decided to include `next_step` for
|
|
107
111
|
|
108
112
|
== Remaining Work
|
109
113
|
|
110
|
-
Plist4r
|
114
|
+
Plist4r has now moved from alpha to beta - quality software. TBC...
|
111
115
|
|
112
116
|
* Regression Tests (rspec)
|
113
117
|
* Test harness for the backends
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.
|
1
|
+
0.2.0
|
data/lib/plist4r.rb
CHANGED
@@ -18,18 +18,23 @@ module Plist4r
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def string_detect_format string
|
21
|
-
|
22
|
-
|
21
|
+
# puts "in string_detect_format"
|
22
|
+
# puts "string = #{string.inspect}"
|
23
|
+
# s = string.strip
|
24
|
+
string.strip! if string[0,1] =~ /\s/
|
25
|
+
# s = string
|
26
|
+
# puts "s = #{s.inspect}"
|
27
|
+
case string[0,1]
|
23
28
|
when "{","("
|
24
29
|
:next_step
|
25
30
|
when "b"
|
26
|
-
if
|
31
|
+
if string =~ /^bplist/
|
27
32
|
:binary
|
28
33
|
else
|
29
34
|
nil
|
30
35
|
end
|
31
36
|
when "<"
|
32
|
-
if
|
37
|
+
if string =~ /^\<\?xml/ && string =~ /\<\!DOCTYPE plist/
|
33
38
|
:xml
|
34
39
|
else
|
35
40
|
nil
|
data/lib/plist4r/backend.rb
CHANGED
@@ -21,11 +21,11 @@ module Plist4r
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
# vv We
|
24
|
+
# vv We need a version of this to call our matrix test harness vv
|
25
25
|
|
26
26
|
def call method_sym, *args, &blk
|
27
|
-
puts "in call"
|
28
|
-
puts "#{method_sym.inspect} #{args.inspect}"
|
27
|
+
# puts "in call"
|
28
|
+
# puts "#{method_sym.inspect} #{args.inspect}"
|
29
29
|
raise "Unsupported api call #{method_sym.inspect}" unless ApiMethods.include? method_sym.to_s
|
30
30
|
exceptions = []
|
31
31
|
@backends.each do |backend|
|
@@ -3,15 +3,16 @@ require 'plist4r/backend_base'
|
|
3
3
|
|
4
4
|
module Plist4r::Backend::Example
|
5
5
|
class << self
|
6
|
-
def from_string plist
|
7
|
-
|
6
|
+
def from_string plist
|
7
|
+
plist_string = plist.from_string
|
8
|
+
plist_format = Plist4r.string_detect_format plist.from_string
|
8
9
|
unless [:supported_fmt1,:supported_fmt2].include? plist_format
|
9
10
|
raise "#{self} - cant convert string of format #{plist_format}"
|
10
11
|
end
|
11
12
|
hash = ::ActiveSupport::OrderedHash.new
|
12
13
|
# import / convert plist data into ruby ordered hash
|
13
14
|
plist.import_hash hash
|
14
|
-
plist.file_format
|
15
|
+
plist.file_format plist_format
|
15
16
|
return plist
|
16
17
|
end
|
17
18
|
|
@@ -43,7 +44,7 @@ module Plist4r::Backend::Example
|
|
43
44
|
hash = ::ActiveSupport::OrderedHash.new
|
44
45
|
# import / convert plist data into ruby ordered hash
|
45
46
|
plist.import_hash hash
|
46
|
-
plist.file_format
|
47
|
+
plist.file_format file_format
|
47
48
|
return plist
|
48
49
|
end
|
49
50
|
|
@@ -2,18 +2,19 @@
|
|
2
2
|
require 'plist4r/backend_base'
|
3
3
|
|
4
4
|
module Plist4r::Backend::Plutil
|
5
|
-
# maybe this
|
5
|
+
# maybe this could be useful as a helper, used by other backends
|
6
6
|
class << self
|
7
|
-
|
8
|
-
|
7
|
+
def convert_file_to_xml
|
8
|
+
system "plutil -convert xml1 #{@filename}"
|
9
|
+
end
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
def convert_file_to_binary
|
12
|
+
system "plutil -convert binary1 #{@filename}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def validate
|
16
|
+
system "plutil #{@filename}"
|
17
|
+
end
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
@@ -6,16 +6,17 @@ module Plist4r::Backend::RubyCocoa
|
|
6
6
|
def ruby_cocoa_wrapper_rb
|
7
7
|
@ruby_cocoa_wrapper_rb ||= <<-'EOC'
|
8
8
|
#!/usr/bin/ruby
|
9
|
+
raise "No path given to plist4r" unless ARGV[0] && File.exists?("#{ARGV[0]}/plist4r.rb")
|
9
10
|
|
10
|
-
|
11
|
-
|
12
|
-
ordered_hash_rb = ARGV[0]
|
11
|
+
dir = ARGV[0]
|
12
|
+
$LOAD_PATH.unshift dir unless $LOAD_PATH.include?(dir)
|
13
13
|
|
14
|
-
require
|
14
|
+
require 'plist4r/mixin/ordered_hash'
|
15
|
+
require 'osx/cocoa'
|
15
16
|
|
16
17
|
class OSX::NSObject
|
17
18
|
def to_ruby
|
18
|
-
case self
|
19
|
+
case self
|
19
20
|
when OSX::NSDate
|
20
21
|
self.to_time
|
21
22
|
when OSX::NSCFBoolean
|
@@ -45,12 +46,13 @@ end
|
|
45
46
|
module Plist
|
46
47
|
def to_xml hash
|
47
48
|
# to_plist defaults to NSPropertyListXMLFormat_v1_0
|
48
|
-
x = hash.
|
49
|
+
x = hash.to_plist
|
49
50
|
puts "#{x}"
|
50
51
|
end
|
52
|
+
|
51
53
|
def to_binary hash
|
52
54
|
# Here 200 == NSPropertyListBinaryFormat_v1_0
|
53
|
-
x = hash.
|
55
|
+
x = hash.to_plist 200
|
54
56
|
puts "#{x}"
|
55
57
|
end
|
56
58
|
|
@@ -78,15 +80,8 @@ class RubyCocoaWrapper
|
|
78
80
|
include Plist
|
79
81
|
|
80
82
|
def exec stdin
|
81
|
-
|
82
|
-
|
83
|
-
instance_eval stdin
|
84
|
-
exit 0
|
85
|
-
rescue LoadError
|
86
|
-
raise $!
|
87
|
-
rescue
|
88
|
-
raise $!
|
89
|
-
end
|
83
|
+
instance_eval stdin
|
84
|
+
exit 0
|
90
85
|
end
|
91
86
|
end
|
92
87
|
|
@@ -104,16 +99,16 @@ EOC
|
|
104
99
|
require 'plist4r/mixin/popen4'
|
105
100
|
|
106
101
|
unless @rb_script && File.exists?(@rb_script.path)
|
107
|
-
@rb_script
|
108
|
-
|
109
|
-
|
102
|
+
@rb_script = Tempfile.new "ruby_cocoa_wrapper.rb."
|
103
|
+
@rb_script.puts ruby_cocoa_wrapper_rb
|
104
|
+
@rb_script.close
|
110
105
|
File.chmod 0755, @rb_script.path
|
111
106
|
end
|
112
|
-
|
107
|
+
|
108
|
+
plist4r_root = File.expand_path File.join(File.dirname(__FILE__), "..", "..")
|
113
109
|
cmd = @rb_script.path
|
114
|
-
ordered_hash_rb = File.join(File.dirname(__FILE__), "..", "mixin", "ordered_hash.rb")
|
115
110
|
|
116
|
-
pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 cmd,
|
111
|
+
pid, stdin, stdout, stderr = ::Plist4r::Popen4::popen4 [cmd, plist4r_root]
|
117
112
|
|
118
113
|
stdin.puts stdin_str
|
119
114
|
|
@@ -126,16 +121,12 @@ EOC
|
|
126
121
|
return [cmd, status, stdout_result, stderr_result]
|
127
122
|
end
|
128
123
|
|
129
|
-
def from_string plist, string
|
130
|
-
raise "method not implemented yet (unfinished)"
|
131
|
-
end
|
132
|
-
|
133
124
|
def to_xml plist
|
134
125
|
hash = plist.to_hash
|
135
|
-
result = ruby_cocoa_exec "to_xml(
|
126
|
+
result = ruby_cocoa_exec "to_xml(#{hash.inspect})"
|
136
127
|
case result[1].exitstatus
|
137
128
|
when 0
|
138
|
-
xml_string =
|
129
|
+
xml_string = result[2]
|
139
130
|
return xml_string
|
140
131
|
else
|
141
132
|
$stderr.puts result[3]
|
@@ -145,40 +136,53 @@ EOC
|
|
145
136
|
|
146
137
|
def to_binary plist
|
147
138
|
hash = plist.to_hash
|
148
|
-
result = ruby_cocoa_exec "to_binary(
|
139
|
+
result = ruby_cocoa_exec "to_binary(#{hash.inspect})"
|
149
140
|
case result[1].exitstatus
|
150
141
|
when 0
|
151
|
-
binary_string =
|
142
|
+
binary_string = result[2]
|
152
143
|
return binary_string
|
153
144
|
else
|
154
145
|
$stderr.puts result[3]
|
155
146
|
raise "Error executing #{result[0]}. See stderr for more information"
|
156
147
|
end
|
157
148
|
end
|
158
|
-
|
159
|
-
def
|
160
|
-
filename = plist.filename
|
149
|
+
|
150
|
+
def open_with_args plist, filename
|
161
151
|
result = ruby_cocoa_exec "open(\"#{filename}\")"
|
162
152
|
case result[1].exitstatus
|
163
153
|
when 0
|
164
|
-
hash =
|
154
|
+
hash = ::ActiveSupport::OrderedHash.new
|
155
|
+
eval("hash.replace("+result[2]+")")
|
165
156
|
plist.import_hash hash
|
166
157
|
else
|
167
158
|
$stderr.puts result[3]
|
168
159
|
raise "Error executing #{result[0]}. See stderr for more information"
|
169
160
|
end
|
170
161
|
file_format = Plist4r.file_detect_format filename
|
171
|
-
plist.file_format
|
162
|
+
plist.file_format file_format
|
172
163
|
return plist
|
173
164
|
end
|
174
165
|
|
166
|
+
def from_string plist
|
167
|
+
require 'tempfile'
|
168
|
+
tf = Tempfile.new "from_string.plist."
|
169
|
+
tf.write plist.from_string
|
170
|
+
tf.close
|
171
|
+
filename = tf.path
|
172
|
+
return open_with_args plist, filename
|
173
|
+
end
|
174
|
+
|
175
|
+
def open plist
|
176
|
+
return open_with_args plist, plist.filename
|
177
|
+
end
|
178
|
+
|
175
179
|
def save hash, filename, file_format
|
176
180
|
filename = plist.filename_path
|
177
181
|
file_format = plist.file_format || Config[:default_format]
|
178
182
|
raise "#{self} - cant save file of format #{file_format}" unless [:xml,:binary].include? file_format
|
179
183
|
|
180
184
|
hash = plist.to_hash
|
181
|
-
result = ruby_cocoa_exec "save(\"#{hash}\",#{filename},#{file_format})"
|
185
|
+
result = ruby_cocoa_exec "save(\"#{hash.inspect}\",#{filename},#{file_format})"
|
182
186
|
case result[1].exitstatus
|
183
187
|
when 0
|
184
188
|
return true
|
data/lib/plist4r/config.rb
CHANGED
@@ -7,15 +7,18 @@ module Plist4r
|
|
7
7
|
class Config
|
8
8
|
extend Mixlib::Config
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
10
|
+
types [] << Dir.glob(File.dirname(__FILE__) + "/plist_type/**/*.rb").collect {|b| File.basename(b,".rb") }
|
11
|
+
types.flatten!.uniq!
|
12
|
+
|
13
|
+
backends ["ruby_cocoa","haml","libxml4r"]
|
14
|
+
backends << Dir.glob(File.dirname(__FILE__) + "/backend/**/*.rb").collect {|b| File.basename(b,".rb") }
|
15
|
+
backends.flatten!.uniq!
|
15
16
|
|
16
17
|
unsupported_keys true
|
17
|
-
raise_any_failure
|
18
|
+
raise_any_failure true
|
18
19
|
deafult_format :xml
|
19
20
|
default_path nil
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
24
|
+
|
@@ -1,4 +1,5 @@
|
|
1
1
|
|
2
|
+
require 'plist4r/config'
|
2
3
|
require 'plist4r/mixin/ordered_hash'
|
3
4
|
|
4
5
|
module Plist4r
|
@@ -21,13 +22,26 @@ module Plist4r
|
|
21
22
|
end
|
22
23
|
|
23
24
|
def method_missing method_symbol, *args, &blk
|
24
|
-
puts "method_missing: #{method_symbol.inspect}, args: #{args.inspect}"
|
25
|
+
# puts "method_missing: #{method_symbol.inspect}, args: #{args.inspect}"
|
26
|
+
# puts "@hash = #{@hash.inspect}"
|
27
|
+
# puts "hi"
|
25
28
|
valid_keys.each do |key_type, valid_keys_of_those_type|
|
26
29
|
if valid_keys_of_those_type.include?(method_symbol.to_s.camelcase)
|
27
30
|
puts "key_type = #{key_type}, method_symbol.to_s.camelcase = #{method_symbol.to_s.camelcase}, args = #{args.inspect}"
|
28
|
-
return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
|
31
|
+
# return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
|
32
|
+
return set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk
|
29
33
|
end
|
30
34
|
end
|
35
|
+
# puts "there"
|
36
|
+
# puts @plist.inspect
|
37
|
+
if @plist.unsupported_keys
|
38
|
+
key_type = nil
|
39
|
+
# return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
|
40
|
+
return set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk
|
41
|
+
else
|
42
|
+
raise "Unrecognized key for class: #{self.class.inspect}. Tried to set_or_return #{method_symbol.inspect}, with: #{args.inspect}"
|
43
|
+
end
|
44
|
+
# puts "bob"
|
31
45
|
end
|
32
46
|
|
33
47
|
def validate_value key_type, key, value
|
@@ -53,7 +67,7 @@ module Plist4r
|
|
53
67
|
end
|
54
68
|
|
55
69
|
def set_or_return key_type, key, value=nil
|
56
|
-
puts "#{method_name}, key_type: #{key_type.inspect}, value: #{value.inspect}"
|
70
|
+
# puts "#{method_name}, key_type: #{key_type.inspect}, key: #{key.inspect}, value: #{value.inspect}"
|
57
71
|
if value
|
58
72
|
validate_value key_type, key, value unless key_type == nil
|
59
73
|
@hash[key] = value
|
@@ -62,4 +76,8 @@ module Plist4r
|
|
62
76
|
end
|
63
77
|
end
|
64
78
|
end
|
65
|
-
end
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
|
83
|
+
|
data/lib/plist4r/plist.rb
CHANGED
@@ -8,14 +8,16 @@ require 'plist4r/backend'
|
|
8
8
|
|
9
9
|
module Plist4r
|
10
10
|
class Plist
|
11
|
-
PlistOptionsHash = %w[
|
11
|
+
PlistOptionsHash = %w[filename path file_format plist_type unsupported_keys backends from_string]
|
12
12
|
FileFormats = %w[binary xml next_step]
|
13
13
|
|
14
14
|
def initialize *args, &blk
|
15
15
|
@hash = ::ActiveSupport::OrderedHash.new
|
16
|
-
|
16
|
+
@plist_type = plist_type :plist
|
17
|
+
|
17
18
|
@unsupported_keys = Config[:unsupported_keys]
|
18
19
|
@backends = Config[:backends]
|
20
|
+
|
19
21
|
@from_string = nil
|
20
22
|
@filename = nil
|
21
23
|
@file_format = nil
|
@@ -32,19 +34,20 @@ module Plist4r
|
|
32
34
|
raise "Unrecognized first argument: #{args.first.inspect}"
|
33
35
|
end
|
34
36
|
|
35
|
-
@plist_cache
|
37
|
+
@plist_cache ||= PlistCache.new self
|
36
38
|
end
|
37
39
|
|
38
40
|
def from_string string=nil
|
39
41
|
case string
|
40
42
|
when String
|
41
|
-
plist_format = ::Plist4r.string_detect_format
|
43
|
+
plist_format = ::Plist4r.string_detect_format(string)
|
42
44
|
if plist_format
|
43
|
-
|
45
|
+
@from_string = string
|
46
|
+
@plist_cache ||= PlistCache.new self
|
47
|
+
@plist_cache.from_string
|
44
48
|
else
|
45
49
|
raise "Unknown plist format for string: #{string}"
|
46
50
|
end
|
47
|
-
@from_string = string
|
48
51
|
when nil
|
49
52
|
@from_string
|
50
53
|
else
|
@@ -101,25 +104,63 @@ module Plist4r
|
|
101
104
|
end
|
102
105
|
end
|
103
106
|
|
107
|
+
def detect_plist_type
|
108
|
+
stat_m = {}
|
109
|
+
stat_r = {}
|
110
|
+
Config[:types].each do |t|
|
111
|
+
case t
|
112
|
+
when String, Symbol
|
113
|
+
t = eval "::Plist4r::PlistType::#{t.to_s.camelcase}"
|
114
|
+
when Class
|
115
|
+
t = t
|
116
|
+
else
|
117
|
+
raise "Unrecognized plist type: #{t.inspect}"
|
118
|
+
end
|
119
|
+
t_sym = t.to_s.gsub(/.*:/,"").snake_case.to_sym
|
120
|
+
stat_t = t.match_stat @hash.keys
|
121
|
+
|
122
|
+
stat_m.store stat_t[:matches], t_sym
|
123
|
+
stat_r.store stat_t[:ratio], t_sym
|
124
|
+
end
|
125
|
+
|
126
|
+
most_matches = stat_m.keys.sort.last
|
127
|
+
if most_matches == 0
|
128
|
+
plist_type :plist
|
129
|
+
elsif stat_m.keys.select{ |m| m == most_matches }.size > 1
|
130
|
+
most_matches = stat_r.keys.sort.last
|
131
|
+
if stat_r.keys.select{ |m| m == most_matches }.size > 1
|
132
|
+
plist_type :plist
|
133
|
+
else
|
134
|
+
plist_type stat_r[most_matches]
|
135
|
+
end
|
136
|
+
else
|
137
|
+
plist_type stat_m[most_matches]
|
138
|
+
end
|
139
|
+
return true
|
140
|
+
end
|
141
|
+
|
104
142
|
def plist_type plist_type=nil
|
105
143
|
begin
|
106
144
|
case plist_type
|
107
145
|
when Class
|
108
|
-
|
146
|
+
unless plist_type.is_a? ::Plist4r::PlistType
|
147
|
+
raise "Unrecognized Plist type. Class #{plist_type.inspect} isnt inherited from ::Plist4r::PlistType"
|
148
|
+
end
|
109
149
|
when Symbol, String
|
110
|
-
eval "
|
111
|
-
@plist_type = pt_klass.new :hash => @hash
|
150
|
+
plist_type = eval "::Plist4r::PlistType::#{plist_type.to_s.camelcase}"
|
112
151
|
when nil
|
113
|
-
@plist_type
|
152
|
+
return @plist_type.to_sym
|
114
153
|
else
|
115
154
|
raise "Please specify a valid plist class name, eg ::Plist4r::PlistType::ClassName, \"class_name\" or :class_name"
|
116
155
|
end
|
156
|
+
@plist_type = plist_type.new self
|
157
|
+
return @plist_type.to_sym
|
117
158
|
rescue
|
118
159
|
raise "Please specify a valid plist class name, eg ::Plist4r::PlistType::ClassName, \"class_name\" or :class_name"
|
119
160
|
end
|
120
161
|
end
|
121
|
-
|
122
|
-
def unsupported_keys bool
|
162
|
+
|
163
|
+
def unsupported_keys bool=nil
|
123
164
|
case bool
|
124
165
|
when true,false
|
125
166
|
@unsupported_keys = bool
|
@@ -143,14 +184,16 @@ module Plist4r
|
|
143
184
|
|
144
185
|
def parse_opts opts
|
145
186
|
PlistOptionsHash.each do |opt|
|
146
|
-
|
187
|
+
if opts[opt.to_sym]
|
188
|
+
value = opts[opt.to_sym]
|
189
|
+
eval "self.#{opt}(value)"
|
190
|
+
end
|
147
191
|
end
|
148
192
|
end
|
149
193
|
|
150
194
|
def open filename=nil
|
151
195
|
@filename = filename if filename
|
152
196
|
raise "No filename specified" unless @filename
|
153
|
-
# @hash = @plist_cache.open
|
154
197
|
@plist_cache.open
|
155
198
|
end
|
156
199
|
|
@@ -159,7 +202,10 @@ module Plist4r
|
|
159
202
|
end
|
160
203
|
|
161
204
|
def edit *args, &blk
|
162
|
-
|
205
|
+
@plist_type.hash @hash
|
206
|
+
instance_eval *args, &blk
|
207
|
+
detect_plist_type
|
208
|
+
@plist_cache.update_checksum
|
163
209
|
end
|
164
210
|
|
165
211
|
def method_missing method_sym, *args, &blk
|
@@ -227,37 +273,24 @@ module Plist4r
|
|
227
273
|
instance_eval(&@block) if @block
|
228
274
|
end
|
229
275
|
|
276
|
+
def override_plist_keys?
|
277
|
+
return true unless @label == @filename.match(/^.*\/(.*)\.plist$/)[1]
|
278
|
+
vars = self.instance_variables - ["@filename","@label","@shortname","@block","@hash","@obj"]
|
279
|
+
return true unless vars.empty?
|
280
|
+
end
|
281
|
+
|
230
282
|
def finalize
|
231
283
|
if File.exists? @filename
|
232
284
|
if override_plist_keys?
|
233
285
|
# @hash = @obj = ::LibxmlLaunchdPlistParser.new(@filename).plist_struct
|
234
286
|
# eval_plist_block(&@block) if @block
|
235
|
-
write_plist
|
287
|
+
# write_plist
|
236
288
|
end
|
237
289
|
else
|
238
|
-
write_plist
|
290
|
+
# write_plist
|
239
291
|
end
|
240
|
-
|
241
|
-
end
|
242
|
-
|
243
|
-
def override_plist_keys?
|
244
|
-
return true unless @label == @filename.match(/^.*\/(.*)\.plist$/)[1]
|
245
|
-
vars = self.instance_variables - ["@filename","@label","@shortname","@block","@hash","@obj"]
|
246
|
-
return true unless vars.empty?
|
247
|
-
end
|
248
|
-
|
249
|
-
def write_plist
|
250
|
-
require 'haml'
|
251
|
-
engine = Haml::Engine.new File.read("launchd_plist.haml")
|
252
|
-
rendered_xml_output = engine.render self
|
253
|
-
File.open(@filename,'w') do |o|
|
254
|
-
o << rendered_xml_output
|
255
|
-
end
|
256
|
-
|
257
|
-
end
|
258
|
-
|
259
|
-
def validate
|
260
|
-
system "/usr/bin/plutil #{@filename}"
|
261
|
-
end
|
292
|
+
end
|
262
293
|
end
|
263
294
|
end
|
295
|
+
|
296
|
+
|
data/lib/plist4r/plist_cache.rb
CHANGED
@@ -23,19 +23,25 @@ module Plist4r
|
|
23
23
|
checksum != last_checksum
|
24
24
|
end
|
25
25
|
|
26
|
+
def from_string
|
27
|
+
@backend.call :from_string
|
28
|
+
update_checksum
|
29
|
+
@plist.detect_plist_type
|
30
|
+
@plist
|
31
|
+
end
|
32
|
+
|
26
33
|
def to_xml
|
27
|
-
|
28
|
-
# if needs_update
|
34
|
+
if needs_update || @xml.nil?
|
29
35
|
puts "needs update"
|
30
36
|
update_checksum
|
31
37
|
@xml = @backend.call :to_xml
|
32
|
-
|
33
|
-
|
34
|
-
|
38
|
+
else
|
39
|
+
@xml
|
40
|
+
end
|
35
41
|
end
|
36
42
|
|
37
43
|
def to_binary
|
38
|
-
if needs_update
|
44
|
+
if needs_update || @binary.nil?
|
39
45
|
update_checksum
|
40
46
|
@binary = @backend.call :to_binary
|
41
47
|
else
|
@@ -44,7 +50,7 @@ module Plist4r
|
|
44
50
|
end
|
45
51
|
|
46
52
|
def to_next_step
|
47
|
-
if needs_update
|
53
|
+
if needs_update || @next_step.nil?
|
48
54
|
update_checksum
|
49
55
|
@next_step = @backend.call :to_next_step
|
50
56
|
else
|
@@ -55,6 +61,7 @@ module Plist4r
|
|
55
61
|
def open
|
56
62
|
@backend.call :open
|
57
63
|
update_checksum
|
64
|
+
@plist.detect_plist_type
|
58
65
|
@plist
|
59
66
|
end
|
60
67
|
|
data/lib/plist4r/plist_type.rb
CHANGED
@@ -5,9 +5,43 @@ module Plist4r
|
|
5
5
|
class PlistType
|
6
6
|
include ::Plist4r::DataMethods
|
7
7
|
|
8
|
-
def initialize
|
9
|
-
|
10
|
-
@hash = @orig =
|
8
|
+
def initialize plist, *args, &blk
|
9
|
+
@plist = plist
|
10
|
+
@hash = @orig = plist.to_hash
|
11
|
+
end
|
12
|
+
|
13
|
+
def hash hash=nil
|
14
|
+
case hash
|
15
|
+
when ::ActiveSupport::OrderedHash
|
16
|
+
@hash = @orig = hash
|
17
|
+
when nil
|
18
|
+
@hash
|
19
|
+
else
|
20
|
+
raise "Must hash be an ::ActiveSupport::OrderedHash"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.valid_keys
|
25
|
+
raise "Method not implemented #{method_name.to_sym.inspect}, for class #{self.inspect}"
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid_keys
|
29
|
+
self.class.valid_keys
|
30
|
+
end
|
31
|
+
|
32
|
+
def self.match_stat plist_keys
|
33
|
+
type_keys = valid_keys.values.flatten
|
34
|
+
matches = plist_keys & type_keys
|
35
|
+
include_ratio = matches.size.to_f / type_keys.size
|
36
|
+
return :matches => matches.size, :ratio => include_ratio
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_s
|
40
|
+
return @string ||= self.class.to_s.gsub(/.*:/,"").snake_case
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_sym
|
44
|
+
return @sym ||= to_s.to_sym
|
11
45
|
end
|
12
46
|
end
|
13
47
|
|
@@ -4,16 +4,15 @@ require 'plist4r/plist_type'
|
|
4
4
|
module Plist4r
|
5
5
|
class PlistType::Launchd < PlistType
|
6
6
|
|
7
|
-
def valid_keys
|
7
|
+
def self.valid_keys
|
8
8
|
{
|
9
9
|
:string => %w[Label UserName GroupName LimitLoadToSessionType Program RootDirectory WorkingDirectory StandardInPath StandardOutPath StandardErrorPath],
|
10
10
|
:bool => %w[Disabled EnableGlobbing EnableTransactions OnDemand RunAtLoad InitGroups StartOnMount Debug WaitForDebugger AbandonProcessGroup HopefullyExitsFirst HopefullyExitsLast LowPriorityIO LaunchOnlyOnce],
|
11
11
|
:integer => %w[Umask TimeOut ExitTimeOut ThrottleInterval StartInterval Nice],
|
12
12
|
:array_of_strings => %w[LimitLoadToHosts LimitLoadFromHosts ProgramArguments WatchPaths QueueDirectories],
|
13
|
-
:
|
13
|
+
:method_defined => %w[inetdCompatibility KeepAlive EnvironmentVariables StartCalendarInterval SoftResourceLimits, HardResourceLimits MachServices Sockets]
|
14
14
|
}
|
15
15
|
end
|
16
|
-
|
17
16
|
|
18
17
|
# :call-seq:
|
19
18
|
# inetdCompatibility({:wait => true})
|
data/plist4r.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{plist4r}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.2.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["dreamcat4"]
|
12
|
-
s.date = %q{2010-03-
|
12
|
+
s.date = %q{2010-03-18}
|
13
13
|
s.description = %q{In development. Plist4R is a gem which is striving for 3 things: ease of use, speed, and reliability handling of plists. To help achieve these goals, we may plug-in or re-write this gem with one or several backends. Notably, we try to distinguish this gem by providing easy-to use DSL interface for users. For common plist type(s), such as convenience methods for Launchd Plist}
|
14
14
|
s.email = %q{dreamcat4@gmail.com}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -49,11 +49,10 @@ Gem::Specification.new do |s|
|
|
49
49
|
"lib/plist4r/plist_type/launchd.rb",
|
50
50
|
"lib/plist4r/plist_type/plist.rb",
|
51
51
|
"plist4r.gemspec",
|
52
|
-
"plists/
|
53
|
-
"plists/
|
54
|
-
"plists/
|
55
|
-
"plists/
|
56
|
-
"plists/test.plist",
|
52
|
+
"plists/example_big_binary.plist",
|
53
|
+
"plists/example_medium_binary_launchd.plist",
|
54
|
+
"plists/example_medium_launchd.xml",
|
55
|
+
"plists/mini.xml",
|
57
56
|
"spec/examples.rb",
|
58
57
|
"spec/plist4r/plist_spec.rb",
|
59
58
|
"spec/plist4r_spec.rb",
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/test.rb
CHANGED
@@ -2,13 +2,38 @@
|
|
2
2
|
|
3
3
|
require 'lib/plist4r'
|
4
4
|
|
5
|
-
|
5
|
+
# puts "Backends = #{::Plist4r::Config[:backends].inspect}"
|
6
6
|
# Plist4r.new
|
7
7
|
|
8
|
-
p = Plist4r.open "plists/
|
8
|
+
p = Plist4r.open "plists/mini.xml"
|
9
9
|
|
10
10
|
# puts p.inspect
|
11
11
|
# puts p.to_hash.inspect
|
12
|
+
# puts p.to_xml
|
13
|
+
|
14
|
+
# b = p.to_binary
|
15
|
+
# puts b.inspect
|
16
|
+
|
17
|
+
|
18
|
+
# p2 = b.to_plist
|
19
|
+
# puts p2.inspect
|
20
|
+
# puts p2.to_xml
|
21
|
+
|
22
|
+
# puts p2.to_xml
|
23
|
+
# puts "plist type is"
|
24
|
+
# puts p2.plist_type.inspect
|
25
|
+
|
26
|
+
# p2.unsupported_keys false
|
27
|
+
# puts p2.unsupported_keys.inspect
|
28
|
+
# p2.<< do
|
29
|
+
# somekey "append"
|
30
|
+
# end
|
31
|
+
|
32
|
+
# puts p2.to_hash.inspect
|
33
|
+
# puts p2.to_xml
|
34
|
+
|
35
|
+
|
36
|
+
|
37
|
+
|
12
38
|
|
13
|
-
puts p.to_xml
|
14
39
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: plist4r
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- dreamcat4
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2010-03-
|
12
|
+
date: 2010-03-18 00:00:00 +00:00
|
13
13
|
default_executable:
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -84,11 +84,10 @@ files:
|
|
84
84
|
- lib/plist4r/plist_type/launchd.rb
|
85
85
|
- lib/plist4r/plist_type/plist.rb
|
86
86
|
- plist4r.gemspec
|
87
|
-
- plists/
|
88
|
-
- plists/
|
89
|
-
- plists/
|
90
|
-
- plists/
|
91
|
-
- plists/test.plist
|
87
|
+
- plists/example_big_binary.plist
|
88
|
+
- plists/example_medium_binary_launchd.plist
|
89
|
+
- plists/example_medium_launchd.xml
|
90
|
+
- plists/mini.xml
|
92
91
|
- spec/examples.rb
|
93
92
|
- spec/plist4r/plist_spec.rb
|
94
93
|
- spec/plist4r_spec.rb
|
@@ -1,75 +0,0 @@
|
|
1
|
-
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
-
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
3
|
-
<plist version="1.0">
|
4
|
-
<dict>
|
5
|
-
<key>autoPoll</key>
|
6
|
-
<true/>
|
7
|
-
<key>cMax</key>
|
8
|
-
<integer>129600</integer>
|
9
|
-
<key>debug</key>
|
10
|
-
<true/>
|
11
|
-
<key>debugInXcode</key>
|
12
|
-
<false/>
|
13
|
-
<key>desktopPrefs</key>
|
14
|
-
<dict>
|
15
|
-
<key>BackgroundColor</key>
|
16
|
-
<array>
|
17
|
-
<real>0.254902</real>
|
18
|
-
<real>0.4117647</real>
|
19
|
-
<real>0.6666667</real>
|
20
|
-
</array>
|
21
|
-
<key>Change</key>
|
22
|
-
<string>Never</string>
|
23
|
-
<key>ChangePath</key>
|
24
|
-
<string>/Library/Desktop Pictures/Nature</string>
|
25
|
-
<key>ChangeTime</key>
|
26
|
-
<real>1800</real>
|
27
|
-
<key>DrawBackgroundColor</key>
|
28
|
-
<true/>
|
29
|
-
<key>ImageFileAlias</key>
|
30
|
-
<data>AAAAAAC4AAMAAAAAxY+tngAASCsAAAAAAEh7KwBIe0wAAMaUOpUAAAAACSD//gAAAAAAAAAA/////wABAAwASHsrAEh61QBGMgwADgAUAAkARQBhAHIAdABoAC4AagBwAGcADwAaAAwATQBhAGMAaQBuAHQAbwBzAGgAIABIAEQAEgApTGlicmFyeS9EZXNrdG9wIFBpY3R1cmVzL05hdHVyZS9FYXJ0aC5qcGcAABMAAS8A//8AAA==</data>
|
31
|
-
<key>ImageFilePath</key>
|
32
|
-
<string>/Library/Desktop Pictures/Nature/Earth.jpg</string>
|
33
|
-
<key>NewChangePath</key>
|
34
|
-
<string>/Library/Desktop Pictures/Nature</string>
|
35
|
-
<key>NewImageFilePath</key>
|
36
|
-
<string>/Library/Desktop Pictures/Nature/Earth.jpg</string>
|
37
|
-
<key>Placement</key>
|
38
|
-
<string>Crop</string>
|
39
|
-
<key>Random</key>
|
40
|
-
<false/>
|
41
|
-
</dict>
|
42
|
-
<key>error</key>
|
43
|
-
<data>YnBsaXN0MDDUAQIDBAUIT1BUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJjaGl2ZXLRBgdUcm9vdIABrxAUCQoTFCIjJCUmJy00NTo7RUZHSEtVJG51bGzUCwwNDg8QERJWJGNsYXNzVk5TQ29kZVpOU1VzZXJJbmZvWE5TRG9tYWlugBMT/////////BeAA4ACXxAQTlNVUkxFcnJvckRvbWFpbtMVFgsXHCFaTlMub2JqZWN0c1dOUy5rZXlzpBgZGhuACIAJgAuADKQdHh8ggASABYAGgAeAEl8QGk5TRXJyb3JGYWlsaW5nVVJMU3RyaW5nS2V5XxAUTlNFcnJvckZhaWxpbmdVUkxLZXlfEBZOU0xvY2FsaXplZERlc2NyaXB0aW9uXxARTlNVbmRlcmx5aW5nRXJyb3JfEJFodHRwOi8vcGhvdG9ncmFwaHkubmF0aW9uYWxnZW9ncmFwaGljLmNvbS9zdGF0aWNmaWxlcy9OR1MvU2hhcmVkL1N0YXRpY0ZpbGVzL1Bob3RvZ3JhcGh5L0ltYWdlcy9QT0QvYS9hdG9tLWJvbWItYmlraW5pLWF0b2xsLTYzMzE3OC0xMDA1MDkteGwuanBn0ygpCyoYLFdOUy5iYXNlW05TLnJlbGF0aXZlgACACIAK0i4vMDNYJGNsYXNzZXNaJGNsYXNzbmFtZaIxMlVOU1VSTFhOU09iamVjdFVOU1VSTF8QFlRoZSByZXF1ZXN0IHRpbWVkIG91dC7UCwwNDg83ODmAExP////////8F4AOgA1fEBdrQ0ZFcnJvckRvbWFpbkNGTmV0d29ya9MVFgs8QCGjGRgagAmACIALo0FCQ4APgBCAEYASXxAUTlNFcnJvckZhaWxpbmdVUkxLZXlfEBpOU0Vycm9yRmFpbGluZ1VSTFN0cmluZ0tleV8QFk5TTG9jYWxpemVkRGVzY3JpcHRpb27SLi9JSqJKMlxOU0RpY3Rpb25hcnnSLi9MTqJNMldOU0Vycm9yV05TRXJyb3ISAAGGoF8QD05TS2V5ZWRBcmNoaXZlcgAIABEAFgAfACgAMgA1ADoAPABTAFkAYgBpAHAAewCEAIYAjwCRAJMApgCtALgAwADFAMcAyQDLAM0A0gDUANYA2ADaANwA+QEQASkBPQHRAdgB4AHsAe4B8AHyAfcCAAILAg4CFAIdAiMCPAJFAkcCUAJSAlQCbgJ1AnkCewJ9An8CgwKFAocCiQKLAqICvwLYAt0C4ALtAvIC9QL9AwUDCgAAAAAAAAIBAAAAAAAAAFEAAAAAAAAAAAAAAAAAAAMc</data>
|
44
|
-
<key>filename</key>
|
45
|
-
<string></string>
|
46
|
-
<key>frequency</key>
|
47
|
-
<string>daily</string>
|
48
|
-
<key>hMin</key>
|
49
|
-
<integer>240</integer>
|
50
|
-
<key>ignoreLostConnectivity</key>
|
51
|
-
<false/>
|
52
|
-
<key>imageSource</key>
|
53
|
-
<data>YnBsaXN0MDDUAQIDBAUI2ttUJHRvcFgkb2JqZWN0c1gkdmVyc2lvblkkYXJjaGl2ZXLRBgdUcm9vdIABrxAwCQofICElLC0uNjlBSktMVFlhYmVocHR5eoKGi4ycnZ6foKGnqKyytbm/wMTIzNDVVSRudWxs2gsMDQ4PEBESExQVFhcYGRoYHB0cWGRhdGFGaWxlU3VpZFV0aXRsZVdkYXRhVXJsW2Rlc2NyaXB0aW9uXxAPX29iakluaXRpYWxpemVkWGltYWdlVXJsVXF1ZXJ5ViRjbGFzc1VlcnJvcoADgASAB4ACgAiABoACgACAL4AAXxCRaHR0cDovL3Bob3RvZ3JhcGh5Lm5hdGlvbmFsZ2VvZ3JhcGhpYy5jb20vc3RhdGljZmlsZXMvTkdTL1NoYXJlZC9TdGF0aWNGaWxlcy9QaG90b2dyYXBoeS9JbWFnZXMvUE9EL2EvYXRvbS1ib21iLWJpa2luaS1hdG9sbC02MzMxNzgtMTAwNTA5LXhsLmpwZ18QaC9Vc2Vycy9pZC9QaWN0dXJlIG9mIFRvZGF5L3Rlc3QvcGljdHVyZXMvTmF0aW9uYWwgR2VvZ3JhcGhpYy9hdG9tLWJvbWItYmlraW5pLWF0b2xsLTYzMzE3OC0xMDA1MDkteGwuanBn0iITIyRZTlMuc3RyaW5nXxAoYWJlODJhYmFmY2I2NTM1NzQwNzIxODFlYzMxYzQwMDRjZDBiZjc4YYAF0iYnKClYJGNsYXNzZXNaJGNsYXNzbmFtZaMpKitfEA9OU011dGFibGVTdHJpbmdYTlNTdHJpbmdYTlNPYmplY3QJbxBVAEEAdABvAG0AaQBjACAAQgBvAG0AYgAgAFQAZQBzAHQAIABQAGgAbwB0AG8ALAAgAEIAaQBrAGkAbgBpACAAQQB0AG8AbABsACAAVwBhAGwAbABwAGEAcABlAHIAICATACAATgBhAHQAaQBvAG4AYQBsACAARwBlAG8AZwByAGEAcABoAGkAYwAgAFAAaABvAHQAbwAgAG8AZgAgAHQAaABlACAARABhAHnULzATMTIzNDVfEA9OU0F0dHJpYnV0ZUluZm9cTlNBdHRyaWJ1dGVzWE5TU3RyaW5ngCyACoAugAnSIhM3JF8RAURBdG9tIEJvbWIgVGVzdCwgQmlraW5pIEF0b2xsClBob3RvZ3JhcGggYnkgVS5TLiBOYXZ5ClRoaXMgbW9udGggaW4gUGhvdG8gb2YgdGhlIERheTogSW1hZ2VzIEZyb20gdGhlIE5hdGlvbmFsIEdlb2dyYXBoaWMgQXJjaGl2ZQpUaGUgc2Vjb25kIGF0b21pYyBib21iIHRlc3RlZCBpbiBPcGVyYXRpb24gQ3Jvc3Nyb2FkcwoKRnJvbSB0aGUgdXBjb21pbmcgTmF0aW9uYWwgR2VvZ3JhcGhpYyBib29rIFRoZSBJbWFnZSBDb2xsZWN0aW9uLCBkdWUgZmFsbCAyMDA5LiBTaG9wIGZvciBvdGhlciBwaG90b2dyYXBoeSBib29rcyBmcm9tIE5hdGlvbmFsIEdlb2dyYXBoaWMuCgqABdI6EztAWk5TLm9iamVjdHOkPD0+P4ALgBSAGIAcgCvTOkITQ0ZJV05TLmtleXOiREWADoAQokdIgAyADYATXxAQTlNQYXJhZ3JhcGhTdHlsZVZOU0ZvbnTUTU5PE1AcUlNfEBJOU1BhcmFncmFwaFNwYWNpbmdaTlNUYWJTdG9wc1tOU0FsaWdubWVudCNAGAAAAAAAAIAAEASAD9ImJ1VYo1ZXK18QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxlXxAQTlNQYXJhZ3JhcGhTdHlsZV8QF05TTXV0YWJsZVBhcmFncmFwaFN0eWxl1FpbE1xdXl9gVk5TU2l6ZVZOU05hbWVYTlNmRmxhZ3MjQDYAAAAAAACAEYASEBBfEBFMdWNpZGFHcmFuZGUtQm9sZNImJ2NkomQrVk5TRm9udNImJ2ZnomcrXE5TRGljdGlvbmFyedM6QhNpbEmiamuAFYAWokdIgAyADYAT1E1OTxNxHFJTI0AYAAAAAAAAgACAD9RaWxNcdXZfeCNAJgAAAAAAAIAXgBIQEFxMdWNpZGFHcmFuZGXTOkITe35Jonx9gBmAGqJHSIAMgA2AE9RNTk8TgxxSUyNAGAAAAAAAAIAAgA/UWlsTXIeIX4ojQCYAAAAAAACAG4ASEBBfEBFIZWx2ZXRpY2EtT2JsaXF1ZdM6QhONlEmmjo9rkZKTgCGAIoAWgCWAJ4AoppWWSJhHmoAdgB6ADYAfgAyAIIATW05TVW5kZXJsaW5lVk5TTGlua1dOU0NvbG9yWE5TQ3Vyc29yEAHToqMTHKWmV05TLmJhc2VbTlMucmVsYXRpdmWAAIAjgCRfEG9odHRwOi8vc2hvcC5uYXRpb25hbGdlb2dyYXBoaWMuY29tL2NvdXBvbi5qc3A/Y29kZT1NUjIwMjEyJlVSTD0lMkZqdW1wLmpzcCUzRml0ZW1JRCUzRDQ0NDUlMjZpdGVtVHlwZSUzRFBST0RVQ1TSJiepq6KqK1VOU1VSTFVOU1VSTNOtrhOvsLFVTlNSR0JcTlNDb2xvclNwYWNlTxARMCAwIDAuOTMzMzMzMzM3MwAQAoAm0iYns7SitCtXTlNDb2xvctRNTk8TthxSUyNAGAAAAAAAAIAAgA/TursTvL2+WU5TSG90U3BvdFxOU0N1cnNvclR5cGWAKRAMgCpXezgsIC04fdImJ8HDosIrWE5TQ3Vyc29yWE5TQ3Vyc29y0iYnxcajxscrXk5TTXV0YWJsZUFycmF5V05TQXJyYXnSyRPKy1dOUy5kYXRhTxAQHQAYAUsCYwEUAiABEQMcAYAt0iYnzc6jzs8rXU5TTXV0YWJsZURhdGFWTlNEYXRh0iYn0dSj0tMrXxAZTlNNdXRhYmxlQXR0cmlidXRlZFN0cmluZ18QEk5TQXR0cmlidXRlZFN0cmluZ18QGU5TTXV0YWJsZUF0dHJpYnV0ZWRTdHJpbmfSJifW2aPX2CtdUFRJbWFnZVNvdXJjZVtMT1dlYlNvdXJjZV1QVEltYWdlU291cmNlEgABhqBfEA9OU0tleWVkQXJjaGl2ZXIACAARABYAHwAoADIANQA6ADwAbwB1AIoAkwCXAJ0ApQCxAMMAzADSANkA3wDhAOMA5QDnAOkA6wDtAO8A8QDzAYcB8gH3AgECLAIuAjMCPAJHAksCXQJmAm8CcAMdAyYDOANFA04DUANSA1QDVgNbBKMEpQSqBLUEugS8BL4EwATCBMQEywTTBNYE2ATaBN0E3wThBOME9gT9BQYFGwUmBTIFOwU9BT8FQQVGBUoFZAV3BZEFmgWhBagFsQW6BbwFvgXABdQF2QXcBeMF6AXrBfgF/wYCBgQGBgYJBgsGDQYPBhgGIQYjBiUGLgY3BjkGOwY9BkoGUQZUBlYGWAZbBl0GXwZhBmoGcwZ1BncGgAaJBosGjQaPBqMGqgaxBrMGtQa3BrkGuwa9BsQGxgbIBsoGzAbOBtAG0gbeBuUG7Qb2BvgG/wcHBxMHFQcXBxkHiweQB5MHmQefB6YHrAe5B80HzwfRB9YH2QfhB+oH8wf1B/cH/ggICBUIFwgZCBsIIwgoCCsINAg9CEIIRghVCF0IYghqCH0IfwiECIgIlgidCKIIpgjCCNcI8wj4CPwJCgkWCSQJKQAAAAAAAAIBAAAAAAAAANwAAAAAAAAAAAAAAAAAAAk7</data>
|
54
|
-
<key>lastUpdate</key>
|
55
|
-
<date>2009-10-05T16:37:47Z</date>
|
56
|
-
<key>maxPictures</key>
|
57
|
-
<integer>1</integer>
|
58
|
-
<key>nextDueDate</key>
|
59
|
-
<date>2009-10-05T22:45:00Z</date>
|
60
|
-
<key>pollingInterval</key>
|
61
|
-
<integer>28799</integer>
|
62
|
-
<key>previousPolls</key>
|
63
|
-
<integer>2</integer>
|
64
|
-
<key>ptEnabled</key>
|
65
|
-
<false/>
|
66
|
-
<key>savePath</key>
|
67
|
-
<string>~/Picture of Today/test/pictures/National Geographic</string>
|
68
|
-
<key>selectedSource</key>
|
69
|
-
<integer>4</integer>
|
70
|
-
<key>updateInterval</key>
|
71
|
-
<integer>86400</integer>
|
72
|
-
<key>updateTime</key>
|
73
|
-
<date>2000-01-01T23:45:00Z</date>
|
74
|
-
</dict>
|
75
|
-
</plist>
|