plist4r 0.0.0 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,19 @@
1
+
2
+ module Plist4r::Backend::Plutil
3
+ # maybe this should be a helper, included by other backends
4
+ class << self
5
+ # use tempfile to write out data
6
+ # convert it into target format
7
+
8
+ # plutil -convert xml1 @filename
9
+ # plutil -convert binary1 @filename
10
+ # next step is not supported
11
+
12
+ # def validate
13
+ # system "plutil #{@filename}"
14
+ # end
15
+ end
16
+ end
17
+
18
+
19
+
@@ -0,0 +1,191 @@
1
+
2
+ require 'plist4r/backend'
3
+
4
+ module Plist4r::Backend::RubyCocoa
5
+ class << self
6
+ def ruby_cocoa_wrapper_rb
7
+ @ruby_cocoa_wrapper_rb ||= <<-'EOC'
8
+ #!/usr/bin/ruby
9
+
10
+ include_endpath = "plist4r/mixin/ordered_hash.rb"
11
+ raise "No path given to include #{include_endpath}" unless ARGV[0] && ARGV[0] =~ /#{include_endpath}$/
12
+ ordered_hash_rb = ARGV[0]
13
+
14
+ require ordered_hash_rb
15
+
16
+ class OSX::NSObject
17
+ def to_ruby
18
+ case self
19
+ when OSX::NSDate
20
+ self.to_time
21
+ when OSX::NSCFBoolean
22
+ self.boolValue
23
+ when OSX::NSNumber
24
+ self.integer? ? self.to_i : self.to_f
25
+ when OSX::NSString
26
+ self.to_s
27
+ when OSX::NSAttributedString
28
+ self.string.to_s
29
+ when OSX::NSArray
30
+ self.to_a.map { |x| x.is_a?(OSX::NSObject) ? x.to_ruby : x }
31
+ when OSX::NSDictionary
32
+ h = ::ActiveSupport::OrderedHash.new
33
+ self.each do |x, y|
34
+ x = x.to_ruby if x.is_a?(OSX::NSObject)
35
+ y = y.to_ruby if y.is_a?(OSX::NSObject)
36
+ h[x] = y
37
+ end
38
+ h
39
+ else
40
+ self
41
+ end
42
+ end
43
+ end
44
+
45
+ module Plist
46
+ def to_xml hash
47
+ # to_plist defaults to NSPropertyListXMLFormat_v1_0
48
+ x = hash.to_ruby.to_plist
49
+ puts "#{x}"
50
+ end
51
+ def to_binary hash
52
+ # Here 200 == NSPropertyListBinaryFormat_v1_0
53
+ x = hash.to_ruby.to_plist 200
54
+ puts "#{x}"
55
+ end
56
+
57
+ def open filename
58
+ plist_dict = ::OSX::NSDictionary.dictionaryWithContentsOfFile(filename)
59
+ puts "#{plist_dict.to_ruby.inspect}"
60
+ end
61
+
62
+ def save hash, filename, file_format
63
+ case file_format.to_sym
64
+ when :xml
65
+ x = hash.to_plist # NSPropertyListXMLFormat_v1_0
66
+ when :binary
67
+ x = hash.to_plist 200 # NSPropertyListBinaryFormat_v1_0
68
+ when :next_step
69
+ raise "File format #{file_format.inspect} is not supported by RubyCocoa"
70
+ else
71
+ raise "File format #{file_format.inspect} not recognised"
72
+ end
73
+ File.open(filename,'w'){ |o| o << x }
74
+ end
75
+ end
76
+
77
+ class RubyCocoaWrapper
78
+ include Plist
79
+
80
+ def exec stdin
81
+ begin
82
+ require 'osx/cocoa'
83
+ instance_eval stdin
84
+ exit 0
85
+ rescue LoadError
86
+ raise $!
87
+ rescue
88
+ raise $!
89
+ end
90
+ end
91
+ end
92
+
93
+ stdin = $stdin.read()
94
+ wrapper = RubyCocoaWrapper.new()
95
+ wrapper.exec stdin
96
+ EOC
97
+ end
98
+
99
+ def ruby_cocoa_exec stdin_str
100
+ rubycocoa_framework = "/System/Library/Frameworks/RubyCocoa.framework"
101
+ raise "RubyCocoa Framework not found. Searched in: #{rubycocoa_framework}" unless File.exists? rubycocoa_framework
102
+
103
+ require 'tempfile'
104
+ require 'plist4r/mixin/popen4'
105
+
106
+ if @rb_script && File.exists?(@rb_script.path)
107
+ @rb_script ||= Tempfile.new("ruby_cocoa_wrapper.rb") do |o|
108
+ o << ruby_cocoa_rb
109
+ end
110
+ File.chmod 0755, @rb_script.path
111
+ end
112
+
113
+ cmd = @rb_script.path
114
+ ordered_hash_rb = File.join(File.dirname(__FILE__), "..", "mixin", "ordered_hash.rb")
115
+
116
+ pid, stdin, stdout, stderr = Popen4::popen4 [cmd, ordered_hash_rb]
117
+
118
+ stdin.puts stdin_str
119
+
120
+ stdin.close
121
+ ignored, status = Process::waitpid2 pid
122
+
123
+ stdout_result = stdout.read.strip
124
+ stderr_result = stderr.read.strip
125
+
126
+ return [cmd, status, stdout_result, stderr_result]
127
+ end
128
+
129
+ def from_string plist, string
130
+ raise "method not implemented yet (unfinished)"
131
+ end
132
+
133
+ def to_xml plist
134
+ hash = plist.to_hash
135
+ result = ruby_cocoa_exec "to_xml(\"#{hash}\")"
136
+ case result[1].exitstatus
137
+ when 0
138
+ xml_string = eval result[2]
139
+ return xml_string
140
+ else
141
+ $stderr.puts result[3]
142
+ raise "Error executing #{result[0]}. See stderr for more information"
143
+ end
144
+ end
145
+
146
+ def to_binary plist
147
+ hash = plist.to_hash
148
+ result = ruby_cocoa_exec "to_binary(\"#{hash}\")"
149
+ case result[1].exitstatus
150
+ when 0
151
+ binary_string = eval result[2]
152
+ return binary_string
153
+ else
154
+ $stderr.puts result[3]
155
+ raise "Error executing #{result[0]}. See stderr for more information"
156
+ end
157
+ end
158
+
159
+ def open plist
160
+ filename = plist.filename
161
+ result = ruby_cocoa_exec "open(\"#{filename}\")"
162
+ case result[1].exitstatus
163
+ when 0
164
+ hash = eval result[2]
165
+ plist.import_hash hash
166
+ else
167
+ $stderr.puts result[3]
168
+ raise "Error executing #{result[0]}. See stderr for more information"
169
+ end
170
+ file_format = Plist4r.file_detect_format filename
171
+ plist.file_format = file_format
172
+ return plist
173
+ end
174
+
175
+ def save hash, filename, file_format
176
+ filename = plist.filename_path
177
+ file_format = plist.file_format || Config[:default_format]
178
+ raise "#{self} - cant save file of format #{file_format}" unless [:xml,:binary].include? file_format
179
+
180
+ hash = plist.to_hash
181
+ result = ruby_cocoa_exec "save(\"#{hash}\",#{filename},#{file_format})"
182
+ case result[1].exitstatus
183
+ when 0
184
+ return true
185
+ else
186
+ raise "Error executing #{result[0]}. See stderr for more information"
187
+ end
188
+ end
189
+ end
190
+ end
191
+
@@ -0,0 +1,18 @@
1
+
2
+ require 'plist4r/mixin/mixlib_config'
3
+
4
+ class Plist4r::Config
5
+ extend Mixlib::Config
6
+
7
+ backends [
8
+ Plist4r::Backend::RubyCocoa,
9
+ Plist4r::Backend::Haml,
10
+ Plist4r::Backend::Libxml4r
11
+ ]
12
+
13
+ unsupported_keys true
14
+ raise_any_failure false
15
+ deafult_format :xml
16
+ default_path nil
17
+ end
18
+
@@ -0,0 +1,7 @@
1
+
2
+ require 'plist4r/mixin/object'
3
+ require 'plist4r/mixin/string'
4
+ require 'plist4r/mixin/ordered_hash'
5
+ require 'plist4r/mixin/popen4'
6
+ require 'plist4r/mixin/data_methods'
7
+
@@ -0,0 +1,128 @@
1
+ # class_attributes.rb - Class Attributes
2
+ # A Feature-complete alternative to @@
3
+
4
+ class Object
5
+ def deep_clone; Marshal::load(Marshal.dump(self)); end
6
+ end
7
+
8
+ module ClassAttributes
9
+
10
+ def cattr(*args, &block)
11
+ (class << self; self; end).class_eval do
12
+ attr_accessor *args
13
+ end
14
+ @cattr ||= []
15
+ @cattr.concat(args)
16
+ end
17
+
18
+ def iattr(*args, &block)
19
+ (class << self; self; end).class_eval do
20
+ attr_accessor *args
21
+ end
22
+ @iattr ||= []
23
+ @iattr.concat(args)
24
+ end
25
+
26
+ def oattr(*args, &block)
27
+ (class << self; self; end).class_eval do
28
+ attr_accessor *args
29
+ end
30
+ @oattr ||= []
31
+ @oattr.concat(args)
32
+ end
33
+
34
+ def oattr_i(*args, &block)
35
+ (class << self; self; end).class_eval do
36
+ attr_accessor *args
37
+ end
38
+ @oattr_i ||= []
39
+ @oattr_i.concat(args)
40
+ end
41
+
42
+ def co_attr(*args, &block)
43
+ cattr(*args,&block)
44
+ oattr(*args,&block)
45
+ end
46
+
47
+ def co_attr_i(*args, &block)
48
+ iattr(*args,&block)
49
+ oattr_i(*args,&block)
50
+ end
51
+
52
+ alias_method :class_inherited_attribute_shared, :cattr
53
+ alias_method :class_inherited_attribute_independant, :iattr
54
+
55
+ alias_method :object_inherited_attribute_shared, :oattr
56
+ alias_method :object_inherited_attribute_independant, :oattr_i
57
+
58
+ alias_method :class_and_object_shared_inherited_attribute, :co_attr
59
+ alias_method :class_and_object_independant_inherited_attribute, :co_attr_i
60
+
61
+ def inherited(subclass)
62
+ super(subclass) if respond_to?('super')
63
+ iattr.each do |a|
64
+ # puts "a=#{a}"
65
+ subclass.send("#{a}=", send(a).deep_clone)
66
+ subclass.send("iattr", a.to_sym)
67
+ end
68
+ cattr.each do |a|
69
+ subclass.send("#{a}=", send(a))
70
+ subclass.send("cattr", a.to_sym)
71
+ end
72
+ oattr.each do |a|
73
+ subclass.send("oattr", a.to_sym)
74
+ end
75
+ oattr_i.each do |a|
76
+ subclass.send("oattr_i", a.to_sym)
77
+ end
78
+ subclass.send(:inherit) if subclass.respond_to?('inherit')
79
+ end
80
+
81
+ def inherit(*args, &block)
82
+ super if respond_to?('super')
83
+ end
84
+ end
85
+
86
+ module ObjectPreInitialize
87
+ private
88
+ def pre_initialize(*args, &block)
89
+ super if respond_to?('super')
90
+
91
+ class_attrs = self.class.cattr + self.class.iattr
92
+
93
+ self.class.oattr.each do |a|
94
+ sac = self.class.send(a)
95
+ eval "@#{a.to_s}=sac" if class_attrs.include? a
96
+ end
97
+
98
+ self.class.oattr_i.each do |a|
99
+ sac = self.class.send(a)
100
+ eval "@#{a.to_s}=sac.deep_clone" if class_attrs.include? a
101
+ end
102
+ end
103
+
104
+ # def postinitialize(*args, &block)
105
+ # end
106
+ end
107
+
108
+ module OverloadNew
109
+ def new(*args, &block)
110
+ newObj = self.allocate
111
+ newObj.send :extend, ObjectPreInitialize
112
+ newObj.send :pre_initialize, *args, &block
113
+ newObj.send :initialize, *args, &block
114
+ # newObj.send :postinitialize, *args, &block
115
+ return newObj
116
+ end
117
+ end
118
+
119
+ def has_class_attributes
120
+ extend ClassAttributes
121
+ end
122
+
123
+ def has_class_object_attributes
124
+ extend ClassAttributes
125
+ extend OverloadNew
126
+ end
127
+
128
+
@@ -0,0 +1,63 @@
1
+
2
+ require 'plist4r/mixins/ordered_hash'
3
+
4
+ module Plst4r::DataMethods
5
+
6
+ def classes_for_key_type
7
+ {
8
+ :string => [String],
9
+ :bool => [TrueClass,FalseClass],
10
+ :integer => [Fixnum],
11
+ :array_of_strings => [Array],
12
+ :hash_of_bools => [Hash],
13
+ :hash => [Hash],
14
+ :bool_or_string_or_array_of_strings => [TrueClass,FalseClass,String,Array]
15
+ }
16
+ end
17
+
18
+ def valid_keys
19
+ {}
20
+ end
21
+
22
+ def method_missing method_symbol, *args, &blk
23
+ puts "method_missing: #{method_symbol.inspect}, args: #{args.inspect}"
24
+ valid_keys.each do |key_type, valid_keys_of_those_type|
25
+ if valid_keys_of_those_type.include?(method_symbol.to_s.camelcase)
26
+ puts "key_type = #{key_type}, method_symbol.to_s.camelcase = #{method_symbol.to_s.camelcase}, args = #{args.inspect}"
27
+ return eval("set_or_return key_type, method_symbol.to_s.camelcase, *args, &blk")
28
+ end
29
+ end
30
+ end
31
+
32
+ def validate_value key_type, key, value
33
+ unless classes_for_key_type[key_type].include? value.class
34
+ raise "Key: #{key}, value: #{value.inspect} is of type #{value.class}. Should be: #{classes_for_key_type[key_type].join ", "}"
35
+ end
36
+ case key_type
37
+ when :array_of_strings, :bool_or_string_or_array_of_strings
38
+ if value.class == Array
39
+ value.each_index do |i|
40
+ unless value[i].class == String
41
+ raise "Element: #{key}[#{i}], value: #{value[i].inspect} is of type #{value[i].class}. Should be: #{classes_for_key_type[:string].join ", "}"
42
+ end
43
+ end
44
+ end
45
+ when :hash_of_bools
46
+ value.each do |k,v|
47
+ unless [TrueClass,FalseClass].include? v.class
48
+ raise "Key: #{key}[#{k}], value: #{v.inspect} is of type #{v.class}. Should be: #{classes_for_key_type[:bool].join ", "}"
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def set_or_return key_type, key, value=nil
55
+ puts "#{method_name}, key_type: #{key_type.inspect}, value: #{value.inspect}"
56
+ if value
57
+ validate_value key_type, key, value unless key_type == nil
58
+ @hash[key] = value
59
+ else
60
+ @orig[key]
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,178 @@
1
+ #
2
+ # Author:: Adam Jacob (<adam@opscode.com>)
3
+ # Author:: Nuo Yan (<nuo@opscode.com>)
4
+ # Author:: Christopher Brown (<cb@opscode.com>)
5
+ # Copyright:: Copyright (c) 2008 Opscode, Inc.
6
+ # License:: Apache License, Version 2.0
7
+ #
8
+ # Licensed under the Apache License, Version 2.0 (the "License");
9
+ # you may not use this file except in compliance with the License.
10
+ # You may obtain a copy of the License at
11
+ #
12
+ # http://www.apache.org/licenses/LICENSE-2.0
13
+ #
14
+ # Unless required by applicable law or agreed to in writing, software
15
+ # distributed under the License is distributed on an "AS IS" BASIS,
16
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17
+ # See the License for the specific language governing permissions and
18
+ # limitations under the License.
19
+ #
20
+
21
+ class Object # http://whytheluckystiff.net/articles/seeingMetaclassesClearly.html
22
+ def meta_def name, &blk
23
+ (class << self; self; end).instance_eval { define_method name, &blk }
24
+ end
25
+ end
26
+
27
+ module Mixlib
28
+ module Config
29
+
30
+ def self.extended(base)
31
+ class << base; attr_accessor :configuration; end
32
+ base.configuration = Hash.new
33
+ end
34
+
35
+ # Loads a given ruby file, and runs instance_eval against it in the context of the current
36
+ # object.
37
+ #
38
+ # Raises an IOError if the file cannot be found, or is not readable.
39
+ #
40
+ # === Parameters
41
+ # <string>:: A filename to read from
42
+ def from_file(filename)
43
+ self.instance_eval(IO.read(filename), filename, 1)
44
+ end
45
+
46
+ # Pass Mixlib::Config.configure() a block, and it will yield self.configuration.
47
+ #
48
+ # === Parameters
49
+ # <block>:: A block that is sent self.configuration as its argument
50
+ def configure(&block)
51
+ block.call(self.configuration)
52
+ end
53
+
54
+ # Get the value of a configuration option
55
+ #
56
+ # === Parameters
57
+ # config_option<Symbol>:: The configuration option to return
58
+ #
59
+ # === Returns
60
+ # value:: The value of the configuration option
61
+ #
62
+ # === Raises
63
+ # <ArgumentError>:: If the configuration option does not exist
64
+ def [](config_option)
65
+ self.configuration[config_option.to_sym]
66
+ end
67
+
68
+ # Set the value of a configuration option
69
+ #
70
+ # === Parameters
71
+ # config_option<Symbol>:: The configuration option to set (within the [])
72
+ # value:: The value for the configuration option
73
+ #
74
+ # === Returns
75
+ # value:: The new value of the configuration option
76
+ def []=(config_option, value)
77
+ internal_set(config_option,value)
78
+ end
79
+
80
+ # Check if Mixlib::Config has a configuration option.
81
+ #
82
+ # === Parameters
83
+ # key<Symbol>:: The configuration option to check for
84
+ #
85
+ # === Returns
86
+ # <True>:: If the configuration option exists
87
+ # <False>:: If the configuration option does not exist
88
+ def has_key?(key)
89
+ self.configuration.has_key?(key.to_sym)
90
+ end
91
+
92
+ # Merge an incoming hash with our config options
93
+ #
94
+ # === Parameters
95
+ # hash<Hash>:: The incoming hash
96
+ #
97
+ # === Returns
98
+ # result of Hash#merge!
99
+ def merge!(hash)
100
+ self.configuration.merge!(hash)
101
+ end
102
+
103
+ # Return the set of config hash keys
104
+ #
105
+ # === Returns
106
+ # result of Hash#keys
107
+ def keys
108
+ self.configuration.keys
109
+ end
110
+
111
+ # Creates a shallow copy of the internal hash
112
+ #
113
+ # === Returns
114
+ # result of Hash#dup
115
+ def hash_dup
116
+ self.configuration.dup
117
+ end
118
+
119
+ # Internal dispatch setter, calling either the real defined method or setting the
120
+ # hash value directly
121
+ #
122
+ # === Parameters
123
+ # method_symbol<Symbol>:: Name of the method (variable setter)
124
+ # value<Object>:: Value to be set in config hash
125
+ #
126
+ def internal_set(method_symbol,value)
127
+ method_name = method_symbol.id2name
128
+ if (self.public_methods - ["[]="]).include?("#{method_name}=")
129
+ self.send("#{method_name}=", value)
130
+ else
131
+ self.configuration[method_symbol] = value
132
+ end
133
+ end
134
+
135
+ protected :internal_set
136
+
137
+ # metaprogramming to ensure that the slot for method_symbol
138
+ # gets set to value after any other logic is run
139
+ # === Parameters
140
+ # method_symbol<Symbol>:: Name of the method (variable setter)
141
+ # blk<Block>:: logic block to run in setting slot method_symbol to value
142
+ # value<Object>:: Value to be set in config hash
143
+ #
144
+ def config_attr_writer(method_symbol, &blk)
145
+ method_name = "#{method_symbol.to_s}="
146
+ meta_def method_name do |value|
147
+ self.configuration[method_symbol] = blk.call(value)
148
+ end
149
+ end
150
+
151
+ # Allows for simple lookups and setting of configuration options via method calls
152
+ # on Mixlib::Config. If there any arguments to the method, they are used to set
153
+ # the value of the configuration option. Otherwise, it's a simple get operation.
154
+ #
155
+ # === Parameters
156
+ # method_symbol<Symbol>:: The method called. Must match a configuration option.
157
+ # *args:: Any arguments passed to the method
158
+ #
159
+ # === Returns
160
+ # value:: The value of the configuration option.
161
+ #
162
+ # === Raises
163
+ # <ArgumentError>:: If the method_symbol does not match a configuration option.
164
+ def method_missing(method_symbol, *args)
165
+ num_args = args.length
166
+ # Setting
167
+ if num_args > 0
168
+ method_symbol = $1.to_sym unless (method_symbol.to_s =~ /(.+)=$/).nil?
169
+ internal_set method_symbol, (num_args == 1 ? args[0] : args)
170
+ end
171
+
172
+ # Returning
173
+ self.configuration[method_symbol]
174
+
175
+ end
176
+ end
177
+ end
178
+