corefoundation 0.2.0 → 0.3.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 0643a8f8fa6e54707a3f01f8ee355d087dbd5c489a5421ce71ff99fb56efe02f
4
+ data.tar.gz: '0168e360dc7c45ea8523198fffd2b133d4de7965f764063a3806caa9ccd37cfb'
5
+ SHA512:
6
+ metadata.gz: 72c31cff02be17a7864bdc57bc77c3cb8af9becad10fa33f0358d9b1765fb3edafce7de53ebfc9fc61c8e9125bca5e0f70e7b20674ad52c555bbeddae1d6c0a3
7
+ data.tar.gz: f70c46f8d528a0783486f0ab7615332bfe52d4fbe841171aefa289fb48675e92bfa48b3c8b164b5d49227bd55f7f8b170059d78dd696242939fe074f39bef9c9
@@ -49,7 +49,7 @@ module CF
49
49
  range[:location] = 0
50
50
  range[:length] = length
51
51
  callback = lambda do |value, _|
52
- yield Base.typecast(value).retain.release_on_gc
52
+ yield Base.typecast(value).retain
53
53
  end
54
54
  CF.CFArrayApplyFunction(self, range, callback, nil)
55
55
  self
@@ -64,13 +64,13 @@ module CF
64
64
  end
65
65
  m = FFI::MemoryPointer.new(:pointer, array.length)
66
66
  m.write_array_of_pointer(array)
67
- new(CF.CFArrayCreate(nil,m,array.length,CF::kCFTypeArrayCallBacks.to_ptr)).release_on_gc
67
+ new(CF.CFArrayCreate(nil,m,array.length,CF::kCFTypeArrayCallBacks.to_ptr))
68
68
  end
69
69
 
70
70
  # Creates a new, empty mutable CFArray
71
71
  # @return [CF::Array] A mutable CF::Array containing the objects, setup to release the array upon garbage collection
72
72
  def self.mutable
73
- result = new(CF.CFArrayCreateMutable nil, 0, CF::kCFTypeArrayCallBacks.to_ptr).release_on_gc
73
+ result = new(CF.CFArrayCreateMutable nil, 0, CF::kCFTypeArrayCallBacks.to_ptr)
74
74
  result.instance_variable_set(:@mutable, true)
75
75
  result
76
76
  end
@@ -79,7 +79,7 @@ module CF
79
79
  # @param [Integer] index the 0 based index of the item to retrieve. Behaviour is undefined if it is not in the range 0...size
80
80
  # @return [CF::Base] a subclass of CF::Base
81
81
  def [](index)
82
- Base.typecast(CF.CFArrayGetValueAtIndex(self, index)).retain.release_on_gc
82
+ Base.typecast(CF.CFArrayGetValueAtIndex(self, index)).retain
83
83
  end
84
84
 
85
85
  # Sets object at the index
@@ -92,7 +92,6 @@ module CF
92
92
  raise TypeError, "instance is not mutable" unless mutable?
93
93
  self.class.check_cftype(value)
94
94
  CF.CFArraySetValueAtIndex(self, index, value)
95
- value
96
95
  end
97
96
 
98
97
  # Appends a value to the array
@@ -1,101 +1,43 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  # The top level namespace for the corefoundation library. The raw FFI generated methods are attached here
3
- #
4
- #
5
4
  module CF
6
- extend FFI::Library
7
- ffi_lib '/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation'
5
+ # The base class for all of the wrapper classes in CF module.
6
+ class Base
7
+ using CF::Refinements
8
8
 
9
- if FFI::Platform::ARCH == 'x86_64'
10
- typedef :long_long, :cfindex
11
- typedef :long_long, :cfcomparisonresult
12
- typedef :ulong_long, :cfoptionflags
13
- typedef :ulong_long, :cftypeid
14
- typedef :ulong_long, :cfhashcode
15
- else
16
- typedef :long, :cfindex
17
- typedef :long, :cfcomparisonresult
18
- typedef :ulong, :cfoptionflags
19
- typedef :ulong, :cftypeid
20
- typedef :ulong, :cfhashcode
21
- end
22
-
23
-
24
- # @private
25
- class Range < FFI::Struct
26
- layout :location, :cfindex,
27
- :length, :cfindex
28
- end
29
-
30
- typedef :pointer, :cftyperef
31
-
32
- #general utility functions
33
- attach_function :show, 'CFShow', [:cftyperef], :void
34
- attach_function :release, 'CFRelease', [:cftyperef], :void
35
- attach_function :retain, 'CFRetain', [:cftyperef], :cftyperef
36
- attach_function 'CFEqual', [:cftyperef, :cftyperef], :char
37
- attach_function 'CFHash', [:cftyperef], :cfhashcode
38
- attach_function 'CFCopyDescription', [:cftyperef], :cftyperef
39
- attach_function 'CFGetTypeID', [:cftyperef], :cftypeid
40
-
41
- # The base class for all of the wrapper classes
42
- #
43
- # @abstract
44
- class Base
45
9
  @@type_map = {}
10
+ include CF::Register
11
+ include CF::Memory
46
12
 
47
- # @private
48
- class Releaser
49
- def initialize(ptr)
50
- @address = ptr.address
51
- end
13
+ attr_reader :ptr
52
14
 
53
- def call *ignored
54
- if @address != 0
55
- CF.release(@address)
56
- @address = 0
57
- end
58
- end
15
+ # Raises an exception if the argument does not inherit from CF::Base
16
+ #
17
+ # @param cftyperef object to check
18
+ def self.check_cftype(cftyperef)
19
+ raise TypeError, "#{cftyperef.inspect} is not a cftype" unless cftyperef.is_a?(CF::Base)
59
20
  end
60
21
 
61
- class << self
62
- # Raises an exception if the argument does not inherit from CF::Base
63
- #
64
- # @param cftyperef object to check
65
- def check_cftype(cftyperef)
66
- raise TypeError, "#{cftyperef.inspect} is not a cftype" unless cftyperef.is_a?(CF::Base)
67
- end
68
-
69
- # Wraps an FFI::Pointer with the appropriate subclass of CF::Base
70
- # If the pointer is not a CFTypeRef behaviour is undefined
71
- # @param [FFI::Pointer] cftyperef Object to wrap
72
- # @return A wrapper object inheriting from CF::Base
73
- def typecast(cftyperef)
74
- klass = klass_from_cf_type cftyperef
75
- klass.new(cftyperef)
76
- end
77
-
78
- private
79
- def klass_from_cf_type cftyperef
80
- klass = @@type_map[CF.CFGetTypeID(cftyperef)]
81
- if !klass
82
- raise TypeError, "No class registered for cf type #{cftyperef.inspect}"
83
- end
84
- klass
85
- end
86
-
87
- def register_type(type_name)
88
- CF.attach_function "#{type_name}GetTypeID", [], :cftypeid
89
- @@type_map[CF.send("#{type_name}GetTypeID")] = self
90
- end
22
+ # Wraps an FFI::Pointer with the appropriate subclass of CF::Base
23
+ # If the pointer is not a CFTypeRef behaviour is undefined
24
+ #
25
+ # @param [FFI::Pointer] cftyperef Object to wrap
26
+ # @return A wrapper object inheriting from CF::Base
27
+ def self.typecast(cftyperef)
28
+ klass = klass_from_cf_type cftyperef
29
+ klass.new(cftyperef)
30
+ end
91
31
 
32
+ # @param [FFI::Pointer] pointer The pointer to wrap
33
+ def initialize(pointer)
34
+ @ptr = FFI::Pointer.new(pointer)
35
+ ObjectSpace.define_finalizer(self, self.class.finalize(ptr))
92
36
  end
93
37
 
94
- #
95
- #
96
- # @param [FFI::Pointer] ptr The pointer to wrap
97
- def initialize(ptr)
98
- @ptr = FFI::Pointer.new(ptr)
38
+ # @param [FFI::Pointer] pointer to the address of the object
39
+ def self.finalize(pointer)
40
+ proc { CF.release(pointer) unless pointer.address.zero }
99
41
  end
100
42
 
101
43
  # Whether the instance is the CFNull singleton
@@ -105,49 +47,6 @@ module CF
105
47
  equals?(CF::NULL)
106
48
  end
107
49
 
108
- # Returns the wrapped pointer
109
- # @return [FFI::Pointer]
110
- def to_ptr
111
- @ptr
112
- end
113
-
114
- # Sets the wrapper pointer. You should only do this rarely. If you have used release_on_gc then that
115
- # finalizer will still be called on the original pointer value
116
- #
117
- def ptr= ptr
118
- @ptr = ptr
119
- end
120
-
121
- # Calls CFRetain on the wrapped pointer
122
- # @return Returns self
123
- def retain
124
- CF.retain(self)
125
- self
126
- end
127
-
128
- # Calls CFRelease on the wrapped pointer
129
- # @return Returns self
130
- def release
131
- CF.release(self)
132
- self
133
- end
134
-
135
- # Installs a finalizer on the wrapper object that will cause it to call CFRelease on the pointer
136
- # when the wrapper is garbage collected
137
- # @return Returns self
138
- def release_on_gc
139
- ObjectSpace.define_finalizer(@ptr, Releaser.new(@ptr))
140
- self
141
- end
142
-
143
- # Returns a ruby string containing the output of CFCopyDescription for the wrapped object
144
- #
145
- # @return [String]
146
- def inspect
147
- cf = CF::String.new(CF.CFCopyDescription(self))
148
- cf.to_s.tap {cf.release}
149
- end
150
-
151
50
  # Uses CFHash to return a hash code
152
51
  #
153
52
  # @return [Integer]
@@ -156,8 +55,6 @@ module CF
156
55
  end
157
56
 
158
57
  # eql? (and ==) are implemented using CFEqual
159
- #
160
- #
161
58
  def eql?(other)
162
59
  if other.is_a?(CF::Base)
163
60
  CF.CFEqual(self, other) != 0
@@ -165,10 +62,10 @@ module CF
165
62
  false
166
63
  end
167
64
  end
168
-
65
+
66
+ alias == :eql?
67
+
169
68
  # equals? is defined as returning true if the wrapped pointer is the same
170
- #
171
- #
172
69
  def equals?(other)
173
70
  if other.is_a?(CF::Base)
174
71
  @ptr.address == other.to_ptr.address
@@ -176,22 +73,21 @@ module CF
176
73
  false
177
74
  end
178
75
  end
179
-
180
- alias_method :==, :eql?
181
76
 
182
- # Converts the CF object into a ruby object. Subclasses typically override this to return
183
- # an appropriate object (for example CF::String returns a String)
184
- #
185
77
  def to_ruby
186
- self
78
+ raise CF::Exceptions::MethodNotImplemented, "#{self.class} should implement the to_ruby method."
187
79
  end
188
80
 
189
- # to_cf is a no-op on CF::Base and its descendants - it always returns self
190
- #
81
+ # This is a no-op on CF::Base and its subclasses. Always returns self.
191
82
  #
83
+ # @return Returns self
192
84
  def to_cf
193
85
  self
194
86
  end
195
- end
196
87
 
88
+ private
89
+ def self.type_map
90
+ @@type_map
91
+ end
92
+ end
197
93
  end
@@ -15,7 +15,7 @@ module CF
15
15
  # @param [String] s the string to use
16
16
  # @return [CF::Data]
17
17
  def self.from_string(s)
18
- new(CF.CFDataCreate(nil, s, s.bytesize)).release_on_gc
18
+ new(CF.CFDataCreate(nil, s, s.bytesize))
19
19
  end
20
20
 
21
21
  # Creates a ruby string from the wrapped data. The encoding will always be ASCII_8BIT
@@ -23,11 +23,7 @@ module CF
23
23
  # @return [String]
24
24
  def to_s
25
25
  ptr = CF.CFDataGetBytePtr(self)
26
- if CF::String::HAS_ENCODING
27
- ptr.read_string(CF.CFDataGetLength(self)).force_encoding(Encoding::ASCII_8BIT)
28
- else
29
- ptr.read_string(CF.CFDataGetLength(self))
30
- end
26
+ ptr.read_string(CF.CFDataGetLength(self)).force_encoding(Encoding::ASCII_8BIT)
31
27
  end
32
28
 
33
29
  # The size in bytes of the CFData
@@ -17,7 +17,7 @@ module CF
17
17
  # @param [Time] time
18
18
  # @return [CF::Date] a CF::Date instance that will be released on garbage collection
19
19
  def self.from_time(time)
20
- new(CF.CFDateCreate(nil, time.to_f - CF.kCFAbsoluteTimeIntervalSince1970)).release_on_gc
20
+ new(CF.CFDateCreate(nil, time.to_f - CF.kCFAbsoluteTimeIntervalSince1970))
21
21
  end
22
22
 
23
23
  # returns a ruby Time instance corresponding to the same point in time
@@ -46,7 +46,7 @@ module CF
46
46
  #
47
47
  # @return [CF::Dictionary]
48
48
  def self.mutable
49
- new(CF.CFDictionaryCreateMutable nil, 0, CF.kCFTypeDictionaryKeyCallBacks.to_ptr, CF.kCFTypeDictionaryValueCallBacks.to_ptr).release_on_gc
49
+ new(CF.CFDictionaryCreateMutable nil, 0, CF.kCFTypeDictionaryKeyCallBacks.to_ptr, CF.kCFTypeDictionaryValueCallBacks.to_ptr)
50
50
  end
51
51
 
52
52
  # Iterates over the array yielding the value to the block
@@ -55,7 +55,7 @@ module CF
55
55
 
56
56
  def each
57
57
  callback = lambda do |key, value, _|
58
- yield [Base.typecast(key).retain.release_on_gc, Base.typecast(value).retain.release_on_gc]
58
+ yield [Base.typecast(key).retain, Base.typecast(value).retain]
59
59
  end
60
60
  CF.CFDictionaryApplyFunction(self, callback, nil)
61
61
  self
@@ -70,7 +70,7 @@ module CF
70
70
  key = CF::String.from_string(key) if key.is_a?(::String)
71
71
  self.class.check_cftype(key)
72
72
  raw = CF.CFDictionaryGetValue(self, key)
73
- raw.null? ? nil : self.class.typecast(raw).retain.release_on_gc
73
+ raw.null? ? nil : self.class.typecast(raw).retain
74
74
  end
75
75
 
76
76
  # Sets the value associated with the key, or nil
@@ -83,7 +83,6 @@ module CF
83
83
  self.class.check_cftype(key)
84
84
  self.class.check_cftype(value)
85
85
  CF.CFDictionarySetValue(self, key, value)
86
- value
87
86
  end
88
87
 
89
88
  # Adds the key value pairs from the argument to self
@@ -0,0 +1,16 @@
1
+ module CF
2
+ module Exceptions
3
+ # Raised when the preference value couldn't be found for a domain.
4
+ class PreferenceDoesNotExist < StandardError
5
+ def initialize(key, domain, hostname = nil)
6
+ super("Returned NULL value for \"#{key}\" in \"#{domain}\"#{", hostname: #{hostname}" if hostname}")
7
+ end
8
+ end
9
+ # Raised when the preference value failed to write.
10
+ class PreferenceSyncFailed < StandardError
11
+ def initialize(key, domain, hostname = nil)
12
+ super("Couldn't write preference value for \"#{key}\" in \"#{domain}\"#{", hostname: #{hostname}" if hostname}")
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The top level namespace for the corefoundation library. The raw FFI generated methods are attached here
4
+ module CF
5
+ attach_function :show, 'CFShow', [:cftyperef], :void
6
+ attach_function :release, 'CFRelease', [:cftyperef], :void
7
+ attach_function :retain, 'CFRetain', [:cftyperef], :cftyperef
8
+ attach_function 'CFEqual', [:cftyperef, :cftyperef], :char
9
+ attach_function 'CFHash', [:cftyperef], :cfhashcode
10
+ attach_function 'CFCopyDescription', [:cftyperef], :cftyperef
11
+ attach_function 'CFGetTypeID', [:cftyperef], :cftypeid
12
+
13
+ # Provides a more declarative way to define all required abstract methods.
14
+ # This gets included in the CF::Base class whose subclasses then need to define those methods.
15
+ module Memory
16
+
17
+ # Returns a ruby string containing the output of CFCopyDescription for the wrapped object
18
+ #
19
+ # @return [String]
20
+ def inspect
21
+ CF::String.new(CF.CFCopyDescription(self)).to_s
22
+ end
23
+
24
+ # Calls CFRelease on the wrapped pointer
25
+ #
26
+ # @return Returns self
27
+ def release
28
+ CF.release(self)
29
+ self
30
+ end
31
+
32
+ # Calls CFRetain on the wrapped pointer
33
+ #
34
+ # @return Returns self
35
+ def retain
36
+ CF.retain(self)
37
+ self
38
+ end
39
+
40
+ # Returns the wrapped pointer
41
+ #
42
+ # @return [FFI::Pointer]
43
+ def to_ptr
44
+ ptr
45
+ end
46
+ end
47
+ end
@@ -39,7 +39,7 @@ module CF
39
39
  def self.from_f(float)
40
40
  p = FFI::MemoryPointer.new(:double)
41
41
  p.write_double(float.to_f)
42
- new(CF.CFNumberCreate(nil, :kCFNumberDoubleType, p)).release_on_gc
42
+ new(CF.CFNumberCreate(nil, :kCFNumberDoubleType, p))
43
43
  end
44
44
 
45
45
  # Constructs a CF::Number from an integer
@@ -48,7 +48,7 @@ module CF
48
48
  def self.from_i(int)
49
49
  p = FFI::MemoryPointer.new(:int64)
50
50
  p.put_int64(0,int.to_i)
51
- new(CF.CFNumberCreate(nil, :kCFNumberSInt64Type, p)).release_on_gc
51
+ new(CF.CFNumberCreate(nil, :kCFNumberSInt64Type, p))
52
52
  end
53
53
 
54
54
  # Compares the receiver with the argument
@@ -0,0 +1,134 @@
1
+ module CF
2
+ typedef :pointer, :cfpropertylistref
3
+ typedef :cfstringref, :key
4
+ typedef :cfstringref, :application_id
5
+ typedef :cfstringref, :username
6
+ typedef :cfstringref, :hostname
7
+ typedef :cfpropertylistref, :value
8
+
9
+ attach_variable 'kCFPreferencesCurrentUser', :cfstringref
10
+ attach_variable 'kCFPreferencesAnyUser', :cfstringref
11
+ attach_variable 'kCFPreferencesCurrentHost', :cfstringref
12
+ attach_variable 'kCFPreferencesAnyHost', :cfstringref
13
+
14
+ attach_function 'CFPreferencesCopyAppValue', [:key, :application_id], :cfpropertylistref
15
+ attach_function 'CFPreferencesCopyValue', [:key, :application_id, :username, :hostname], :cfpropertylistref
16
+
17
+ attach_function 'CFPreferencesSetAppValue', [:key, :value, :application_id], :void
18
+ attach_function 'CFPreferencesSetValue', [:key, :value, :application_id, :username, :hostname], :void
19
+
20
+ attach_function 'CFPreferencesAppSynchronize', [:application_id], :bool
21
+
22
+ # Interface to the preference utilities from Corefoundation.framework.
23
+ # Documentation at https://developer.apple.com/documentation/corefoundation/preferences_utilities
24
+ #
25
+ class Preferences
26
+ using CF::Refinements
27
+
28
+ CURRENT_USER = CF.kCFPreferencesCurrentUser
29
+ ALL_USERS = CF.kCFPreferencesAnyUser
30
+ CURRENT_HOST = CF.kCFPreferencesCurrentHost
31
+ ALL_HOSTS = CF.kCFPreferencesAnyHost
32
+
33
+ class << self
34
+ # Returns the output from `CFPreferencesCopyValue` call after converting it to ruby type.
35
+ #
36
+ # @param [String] key Preference key to read
37
+ # @param [String] application_id Preference domain for the key
38
+ # @param [String, Symbol] username Domain user (current, any or a specific user)
39
+ # @param [String, Symbol] hostname Hostname (current, all hosts or a specific host)
40
+ #
41
+ # @return [VALUE] Preference value returned from the `CFPreferencesCopyValue` call.
42
+ #
43
+ def get(key, application_id, username = nil, hostname = nil)
44
+ plist_ref = if username && hostname
45
+ CF.CFPreferencesCopyValue(
46
+ key.to_cf,
47
+ application_id.to_cf,
48
+ arg_to_cf(username),
49
+ arg_to_cf(hostname)
50
+ )
51
+ else
52
+ CF.CFPreferencesCopyAppValue(key.to_cf, application_id.to_cf)
53
+ end
54
+ CF::Base.typecast(plist_ref).to_ruby unless plist_ref.null?
55
+ end
56
+
57
+ # Calls the {#self.get} method and raise a `PreferenceDoesNotExist` error if `nil` is returned.
58
+ #
59
+ # @param [String] key Preference key to read
60
+ # @param [String] application_id Preference domain for the key
61
+ # @param [String, Symbol] username Domain user (current, any or a specific user)
62
+ # @param [String, Symbol] hostname Hostname (current, all hosts or a specific host)
63
+ #
64
+ # @raise [PreferenceDoesNotExist] If returned value is nil.
65
+ #
66
+ # @return [VALUE] Preference value returned from the {#self.get} method call.
67
+ #
68
+ def get!(key, application_id, username = nil, hostname = nil)
69
+ value = get(key, application_id, username, hostname)
70
+ if value.nil?
71
+ raise(CF::Exceptions::PreferenceDoesNotExist.new(key, application_id, hostname))
72
+ else
73
+ value
74
+ end
75
+ end
76
+
77
+ # Set the value for preference domain using `CFPreferencesSetValue`.
78
+ #
79
+ # @param [String] key Preference key
80
+ # @param [Integer, Float, String, TrueClass, FalseClass, Hash, Array] value Preference value
81
+ # @param [String] application_id Preference domain
82
+ # @param [String, Symbol] username Domain user (current, any or a specific user)
83
+ # @param [String, Symbol] hostname Hostname (current, all hosts or a specific host)
84
+ #
85
+ # @return [TrueClass, FalseClass] Returns true if preference was successfully written to storage, otherwise false.
86
+ #
87
+ def set(key, value, application_id, username = nil, hostname = nil)
88
+ if username && hostname
89
+ CF.CFPreferencesSetValue(
90
+ key.to_cf,
91
+ arg_to_cf(value),
92
+ application_id.to_cf,
93
+ arg_to_cf(username),
94
+ arg_to_cf(hostname)
95
+ )
96
+ else
97
+ CF.CFPreferencesSetAppValue(
98
+ key.to_cf,
99
+ arg_to_cf(value),
100
+ application_id.to_cf
101
+ )
102
+ end
103
+ CF.CFPreferencesAppSynchronize(application_id.to_cf)
104
+ end
105
+
106
+ # Calls the {#self.set} method and raise a `PreferenceSyncFailed` error if `false` is returned.
107
+ #
108
+ # @param [String] key Preference key to write
109
+ # @param [String] application_id Preference domain for the key
110
+ # @param [String, Symbol] username Domain user (current, any or a specific user)
111
+ # @param [String, Symbol] hostname Hostname (current, all hosts or a specific host)
112
+ #
113
+ # @raise [PreferenceSyncFailed] If {#self.set} call returned false.
114
+ #
115
+ # @return [VALUE] Returns nil if preference value is successfully written.
116
+ #
117
+ def set!(key, value, application_id, username = nil, hostname = nil)
118
+ raise(CF::Exceptions::PreferenceSyncFailed.new(key, application_id, hostname)) unless set(key, value, application_id, username, hostname)
119
+ end
120
+
121
+ private
122
+ # Convert an object from ruby to cf type.
123
+ #
124
+ # @param [VALUE, CFType] arg A ruby or corefoundation object.
125
+ #
126
+ # @return [CFType] A wrapped CF object.
127
+ #
128
+ # @visiblity private
129
+ def arg_to_cf(arg)
130
+ arg.respond_to?(:to_cf) ? arg.to_cf : arg
131
+ end
132
+ end
133
+ end
134
+ end
@@ -0,0 +1,10 @@
1
+ module CF
2
+ #
3
+ # Maps to the corefoundation structure CFRange.
4
+ # See usage in CF::Array and CF::String.
5
+ #
6
+ class Range < FFI::Struct
7
+ layout :location, :cfindex,
8
+ :length, :cfindex
9
+ end
10
+ end