corefoundation 0.2.0 → 0.3.4

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.
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