corefoundation 0.1.4 → 0.3.13

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: b4052eded27a6188c93ee482a6d7dfa3d3bf5bcdacbbe9a9801ad1dd86c28790
4
+ data.tar.gz: 2d6dab3b3d788a0671acfd36781739d763014db6c3a864ea64437ed12719e5bc
5
+ SHA512:
6
+ metadata.gz: '09ed7185a6cd6046bed44eef079013b5b15a5df17d70c01ac13aa17dcbcae01df39afb5868e40d70409cdd46136cdc2e92d829503a05341ef2b609f5e29cf84a'
7
+ data.tar.gz: c62e84155e57619823cccc52b9b6ff34cef39d2e657500505bb72b192da809bab178127c70040b6b284dd138bb02377a461d2de65925775d65bc576b87ca7753
@@ -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)
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))
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,44 @@
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))
36
+ CF.retain(ptr)
92
37
  end
93
38
 
94
- #
95
- #
96
- # @param [FFI::Pointer] ptr The pointer to wrap
97
- def initialize(ptr)
98
- @ptr = FFI::Pointer.new(ptr)
39
+ # @param [FFI::Pointer] pointer to the address of the object
40
+ def self.finalize(pointer)
41
+ proc { CF.release(pointer) unless pointer.address.zero? }
99
42
  end
100
43
 
101
44
  # Whether the instance is the CFNull singleton
@@ -105,49 +48,6 @@ module CF
105
48
  equals?(CF::NULL)
106
49
  end
107
50
 
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
51
  # Uses CFHash to return a hash code
152
52
  #
153
53
  # @return [Integer]
@@ -156,8 +56,6 @@ module CF
156
56
  end
157
57
 
158
58
  # eql? (and ==) are implemented using CFEqual
159
- #
160
- #
161
59
  def eql?(other)
162
60
  if other.is_a?(CF::Base)
163
61
  CF.CFEqual(self, other) != 0
@@ -165,10 +63,10 @@ module CF
165
63
  false
166
64
  end
167
65
  end
168
-
66
+
67
+ alias == :eql?
68
+
169
69
  # equals? is defined as returning true if the wrapped pointer is the same
170
- #
171
- #
172
70
  def equals?(other)
173
71
  if other.is_a?(CF::Base)
174
72
  @ptr.address == other.to_ptr.address
@@ -176,22 +74,21 @@ module CF
176
74
  false
177
75
  end
178
76
  end
179
-
180
- alias_method :==, :eql?
181
77
 
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
78
  def to_ruby
186
- self
79
+ raise CF::Exceptions::MethodNotImplemented, "#{self.class} should implement the to_ruby method."
187
80
  end
188
81
 
189
- # to_cf is a no-op on CF::Base and its descendants - it always returns self
190
- #
82
+ # This is a no-op on CF::Base and its subclasses. Always returns self.
191
83
  #
84
+ # @return Returns self
192
85
  def to_cf
193
86
  self
194
87
  end
195
- end
196
88
 
89
+ private
90
+ def self.type_map
91
+ @@type_map
92
+ end
93
+ end
197
94
  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
@@ -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), Base.typecast(value)]
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)
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
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ module CF
4
+ module Refinements
5
+ # Patches for ruby Integer
6
+ refine Integer do
7
+ # Converts the Integer to a {CF::Number} using {CF::Number.from_i}
8
+ # @return [CF::Number]
9
+ def to_cf
10
+ CF::Number.from_i(self)
11
+ end
12
+ end
13
+
14
+ # Patches for ruby Float
15
+ refine Float do
16
+ # Converts the Float to a {CF::Number} using {CF::Number.from_f}
17
+ # @return [CF::Number]
18
+ def to_cf
19
+ CF::Number.from_f(self)
20
+ end
21
+ end
22
+
23
+ # Patches for ruby Array
24
+ refine Array do
25
+ # Converts the Array to an immutable {CF::Array} by calling `to_cf` on each element it contains
26
+ # @return [CF::Number]
27
+ def to_cf
28
+ CF::Array.immutable(collect(&:to_cf))
29
+ end
30
+ end
31
+
32
+ # Patches for ruby TrueClass
33
+ refine TrueClass do
34
+ # Returns a CF::Boolean object representing true
35
+ # @return [CF::Boolean]
36
+ def to_cf
37
+ CF::Boolean::TRUE
38
+ end
39
+ end
40
+
41
+ # Patches for ruby FalseClass
42
+ refine FalseClass do
43
+ # Returns a CF::Boolean object representing false
44
+ # @return [CF::Boolean]
45
+ def to_cf
46
+ CF::Boolean::FALSE
47
+ end
48
+ end
49
+
50
+ # Patches for ruby String
51
+ refine String do
52
+ # Returns a {CF::String} or {CF::Data} representing the string.
53
+ # If {#binary?} returns true a {CF::Data} is returned, if not a {CF::String} is returned
54
+ #
55
+ # If you want a {CF::Data} with the contents of a non binary string, use {#to_cf_data}
56
+ #
57
+ # @return [CF::String, CF::Data]
58
+ def to_cf
59
+ binary? ? to_cf_data : to_cf_string
60
+ end
61
+
62
+ # @!method binary?
63
+ #
64
+ # used to determine whether {#to_cf} should return a {CF::String} or a {CF::Data}.
65
+ # This simply checks whether the encoding is ascii-8bit or not.
66
+ #
67
+ # @return whether the string is handled as binary data or not
68
+ #
69
+ def binary?
70
+ encoding == Encoding::ASCII_8BIT
71
+ end
72
+
73
+ # @param [optional, Boolean, Encoding] bin
74
+ # If you pass `true` then `Encoding::ASCII_BIT` is used, if you pass `false` then `Encoding::UTF_8`
75
+ def binary!(bin = true)
76
+ if bin == true
77
+ force_encoding Encoding::ASCII_8BIT
78
+ else
79
+ # default to utf-8
80
+ force_encoding(bin == false ? "UTF-8" : bin)
81
+ end
82
+ self
83
+ end
84
+
85
+ # Returns a {CF::String} representing the string
86
+ # @return [CF::String]
87
+ def to_cf_string
88
+ CF::String.from_string self
89
+ end
90
+
91
+ # Returns a {CF::Data} representing the string
92
+ # @return [CF::Data]
93
+ def to_cf_data
94
+ CF::Data.from_string self
95
+ end
96
+ end
97
+
98
+ # Patches for ruby Time
99
+ refine Time do
100
+ # Returns a {CF::Date} representing the time.
101
+ # @return [CF::Date]
102
+ def to_cf
103
+ CF::Date.from_time(self)
104
+ end
105
+ end
106
+
107
+ # Patches for ruby Hash
108
+ refine Hash do
109
+ # Converts the Hash to an mutable {CF::Dictionary} by calling `to_cf` on each key and value it contains
110
+ # @return [CF::Dictionary]
111
+ def to_cf
112
+ transform_keys! &:to_s # Convert all keys to strings
113
+ CF::Dictionary.mutable.tap do |r|
114
+ each do |k, v|
115
+ r[k.to_cf] = v.to_cf
116
+ end
117
+ end
118
+ end
119
+ end
120
+ end
121
+ end