corefoundation 0.1.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.
@@ -0,0 +1,32 @@
1
+ module CF
2
+ typedef :pointer, :cfdateref
3
+ typedef :double, :cfabsolutetime
4
+
5
+ attach_function :CFDateCreate, [:pointer, :cfabsolutetime], :cfdateref
6
+ attach_function :CFDateGetAbsoluteTime, [:cfdateref], :cfabsolutetime
7
+ attach_variable :kCFAbsoluteTimeIntervalSince1970, :double
8
+
9
+ # Wrapper for CFDateRef
10
+ #
11
+ #
12
+ class Date < Base
13
+ register_type 'CFDate'
14
+
15
+ # constructs a CF::Date from a ruby time
16
+ #
17
+ # @param [Time] time
18
+ # @return [CF::Date] a CF::Date instance that will be released on garbage collection
19
+ def self.from_time(time)
20
+ new(CF.CFDateCreate(nil, time.to_f - CF.kCFAbsoluteTimeIntervalSince1970)).release_on_gc
21
+ end
22
+
23
+ # returns a ruby Time instance corresponding to the same point in time
24
+ #
25
+ # @return [Time]
26
+ def to_time
27
+ Time.at(CF.CFDateGetAbsoluteTime(self) + CF.kCFAbsoluteTimeIntervalSince1970)
28
+ end
29
+
30
+ alias_method :to_ruby, :to_time
31
+ end
32
+ end
@@ -0,0 +1,114 @@
1
+ module CF
2
+ # @private
3
+ class DictionaryKeyCallbacks < FFI::Struct
4
+ layout :version, :cfindex,
5
+ :retain, :pointer,
6
+ :release, :pointer,
7
+ :copyDescription, :pointer,
8
+ :equal, :pointer,
9
+ :hash, :pointer
10
+ end
11
+ # @private
12
+ class DictionaryValueCallbacks < FFI::Struct
13
+ layout :version, :cfindex,
14
+ :retain, :pointer,
15
+ :release, :pointer,
16
+ :copyDescription, :pointer,
17
+ :equal, :pointer
18
+ end
19
+
20
+ typedef :pointer, :cfdictionaryref
21
+ attach_variable :kCFTypeDictionaryKeyCallBacks, DictionaryKeyCallbacks
22
+ attach_variable :kCFTypeDictionaryValueCallBacks, DictionaryValueCallbacks
23
+ attach_function :CFDictionaryCreateMutable, [:pointer, :cfindex, :pointer, :pointer], :cfdictionaryref
24
+
25
+ attach_function :CFDictionarySetValue, [:cfdictionaryref, :pointer, :pointer], :void
26
+ attach_function :CFDictionaryGetValue, [:cfdictionaryref, :pointer], :pointer
27
+
28
+ attach_function :CFDictionaryGetValue, [:cfdictionaryref, :pointer], :pointer
29
+ attach_function :CFDictionaryGetCount, [:cfdictionaryref], :cfindex
30
+
31
+ callback :each_applier, [:pointer, :pointer, :pointer], :void
32
+
33
+ attach_function :CFDictionaryApplyFunction, [:cfdictionaryref, :each_applier, :pointer], :void
34
+
35
+ # Wrapper class for CFDictionary. It implements Enumerable.
36
+ #
37
+ # Values returned by the accessor methods or yielded by the block are retained and marked as releasable on garbage collection
38
+ # This means you can safely use the returned values even if the CFDictionary itself has been destroyed
39
+ #
40
+ class Dictionary < Base
41
+ register_type("CFDictionary")
42
+ include Enumerable
43
+
44
+ # Return a new, empty mutable CF::Dictionary
45
+ #
46
+ #
47
+ # @return [CF::Dictionary]
48
+ def self.mutable
49
+ new(CF.CFDictionaryCreateMutable nil, 0, CF.kCFTypeDictionaryKeyCallBacks.to_ptr, CF.kCFTypeDictionaryValueCallBacks.to_ptr).release_on_gc
50
+ end
51
+
52
+ # Iterates over the array yielding the value to the block
53
+ # The value is wrapped in the appropriate CF::Base subclass and retained (but marked for releasing upon garbage collection)
54
+ # @return self
55
+
56
+ def each
57
+ callback = lambda do |key, value, _|
58
+ yield [Base.typecast(key).retain.release_on_gc, Base.typecast(value).retain.release_on_gc]
59
+ end
60
+ CF.CFDictionaryApplyFunction(self, callback, nil)
61
+ self
62
+ end
63
+
64
+ # Returns the value associated with the key, or nil
65
+ # The key should be a CF::Base instance. As a convenience, instances of string will be converted to CF::String
66
+ # @param [CF::Base, String] key the key to access
67
+ # @return [CF::Base, nil]
68
+
69
+ def [](key)
70
+ key = CF::String.from_string(key) if key.is_a?(::String)
71
+ self.class.check_cftype(key)
72
+ raw = CF.CFDictionaryGetValue(self, key)
73
+ raw.null? ? nil : self.class.typecast(raw).retain.release_on_gc
74
+ end
75
+
76
+ # Sets the value associated with the key, or nil
77
+ # The key should be a CF::Base instance. As a convenience, instances of string will be converted to CF::String
78
+ # @param [CF::Base, String] key the key to access
79
+ # @param [CF::Base] value the value to set
80
+ # @return [CF::Base] the value that was set
81
+ def []=(key, value)
82
+ key = CF::String.from_string(key) if key.is_a?(::String)
83
+ self.class.check_cftype(key)
84
+ self.class.check_cftype(value)
85
+ CF.CFDictionarySetValue(self, key, value)
86
+ value
87
+ end
88
+
89
+ # Adds the key value pairs from the argument to self
90
+ #
91
+ # @param [CF::Dictionary] other
92
+ # @return self
93
+ def merge!(other)
94
+ other.each do |k,v|
95
+ self[k] = v
96
+ end
97
+ self
98
+ end
99
+
100
+ # Returns the number of key value pairs in the dictionary
101
+ # @return [Integer]
102
+ def length
103
+ CF.CFDictionaryGetCount(self)
104
+ end
105
+
106
+ # Returns a ruby hash constructed by calling `to_ruby` on each key and value
107
+ #
108
+ # @return [Hash]
109
+ def to_ruby
110
+ Hash[collect {|k,v| [k.to_ruby, v.to_ruby]}]
111
+ end
112
+ alias_method :size, :length
113
+ end
114
+ end
@@ -0,0 +1,81 @@
1
+ # Rubyinteger
2
+ class Integer
3
+ # Converts the Integer to a {CF::Number} using {CF::Number.from_i}
4
+ # @return [CF::Number]
5
+ def to_cf
6
+ CF::Number.from_i(self)
7
+ end
8
+ end
9
+
10
+ # Ruby float class
11
+ class Float
12
+ # Converts the Float to a {CF::Number} using {CF::Number.from_f}
13
+ # @return [CF::Number]
14
+ def to_cf
15
+ CF::Number.from_f(self)
16
+ end
17
+ end
18
+
19
+ # Ruby array class
20
+ class Array
21
+ # Converts the Array to an immutable {CF::Array} by calling `to_cf` on each element it contains
22
+ # @return [CF::Number]
23
+ def to_cf
24
+ CF::Array.immutable(collect(&:to_cf))
25
+ end
26
+ end
27
+
28
+ # Ruby true class
29
+ class TrueClass
30
+ # Returns a CF::Boolean object representing true
31
+ # @return [CF::Boolean]
32
+ def to_cf
33
+ CF::Boolean::TRUE
34
+ end
35
+ end
36
+
37
+ # Ruby false class
38
+ class FalseClass
39
+ # Returns a CF::Boolean object representing false
40
+ # @return [CF::Boolean]
41
+ def to_cf
42
+ CF::Boolean::FALSE
43
+ end
44
+ end
45
+
46
+ # Ruby String class
47
+ class String
48
+ # Returns a {CF::String} or {CF::Data} representing the string.
49
+ # If the string has the encoding ASCII_8BIt a {CF::Data} is returned, if not a {CF::String} is returned
50
+ #
51
+ # @return [CF::String, CF::Data]
52
+ def to_cf
53
+ if encoding == Encoding::ASCII_8BIT
54
+ CF::Data.from_string self
55
+ else
56
+ CF::String.from_string self
57
+ end
58
+ end
59
+ end
60
+
61
+ # Ruby Time class
62
+ class Time
63
+ # Returns a {CF::Date} representing the time.
64
+ # @return [CF::Date]
65
+ def to_cf
66
+ CF::Date.from_time(self)
67
+ end
68
+ end
69
+
70
+ # Ruby Hash class
71
+ class Hash
72
+ # Converts the Hash to an mutable {CF::Dictionary} by calling `to_cf` on each key and value it contains
73
+ # @return [CF::Dictionary]
74
+ def to_cf
75
+ CF::Dictionary.mutable.tap do |r|
76
+ each do |k,v|
77
+ r[k.to_cf] = v.to_cf
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,11 @@
1
+ module CF
2
+ attach_variable 'kCFNull', :pointer
3
+
4
+ # Wrapper class for the cf null class
5
+ class Null < Base
6
+ register_type 'CFNull'
7
+ end
8
+
9
+ #The singleton CFNull instance
10
+ NULL = Null.new(CF.kCFNull)
11
+ end
@@ -0,0 +1,98 @@
1
+ module CF
2
+ typedef :pointer, :cfnumberref
3
+ enum :cf_number_type, [
4
+ :kCFNumberSInt8Type,1,
5
+ :kCFNumberSInt16Type,2,
6
+ :kCFNumberSInt32Type,3,
7
+ :kCFNumberSInt64Type,4,
8
+ :kCFNumberFloat32Type,5,
9
+ :kCFNumberFloat64Type,6,
10
+ :kCFNumberCharType,7,
11
+ :kCFNumberShortType,8,
12
+ :kCFNumberIntType,9,
13
+ :kCFNumberLongType,10,
14
+ :kCFNumberLongLongType,11,
15
+ :kCFNumberFloatType,12,
16
+ :kCFNumberDoubleType,13,
17
+ :kCFNumberCFIndexType,14,
18
+ :kCFNumberNSIntegerType,15,
19
+ :kCFNumberCGFloatType,16,
20
+ :kCFNumberMaxType,16
21
+ ]
22
+
23
+ attach_function 'CFNumberGetValue', [:cfnumberref, :cf_number_type, :pointer], :uchar
24
+ attach_function 'CFNumberCreate', [:pointer, :cf_number_type, :pointer], :cfnumberref
25
+ attach_function 'CFNumberIsFloatType', [:pointer], :uchar
26
+ attach_function 'CFNumberCompare', [:cfnumberref, :cfnumberref, :pointer], :cfcomparisonresult
27
+
28
+ # Wrapper for CFNumberRef
29
+ #
30
+ #
31
+ class Number < Base
32
+ register_type 'CFNumber'
33
+ include Comparable
34
+
35
+
36
+ # Constructs a CF::Number from a float
37
+ # @param [Float] float
38
+ # @return [CF::Number]
39
+ def self.from_f(float)
40
+ p = FFI::MemoryPointer.new(:double)
41
+ p.write_double(float.to_f)
42
+ new(CF.CFNumberCreate(nil, :kCFNumberDoubleType, p)).release_on_gc
43
+ end
44
+
45
+ # Constructs a CF::Number from an integer
46
+ # @param [Integer] int
47
+ # @return [CF::Number]
48
+ def self.from_i(int)
49
+ p = FFI::MemoryPointer.new(:int64)
50
+ p.put_int64(0,int.to_i)
51
+ new(CF.CFNumberCreate(nil, :kCFNumberSInt64Type, p)).release_on_gc
52
+ end
53
+
54
+ # Compares the receiver with the argument
55
+ # @param [CF::Number] other
56
+ # @return [Integer]
57
+ def <=>(other)
58
+ raise TypeError, "argument should be CF::Number" unless other.is_a?(CF::Number)
59
+ CF.CFNumberCompare(self,other,nil)
60
+ end
61
+
62
+
63
+ # Converts the CF::Number to either an Integer or a Float, depending on the result of CFNumberIsFloatType
64
+ #
65
+ # @return [Integer, Float]
66
+ def to_ruby
67
+ if CF.CFNumberIsFloatType(self) == 0
68
+ to_i
69
+ else
70
+ to_f
71
+ end
72
+ end
73
+
74
+ # Converts the CF::Number to an integer
75
+ # May raise if the conversion cannot be done without loss of precision
76
+ # @return [Integer]
77
+ def to_i
78
+ p = FFI::MemoryPointer.new(:int64)
79
+ if CF.CFNumberGetValue(self, :kCFNumberSInt64Type, p) != 0
80
+ p.get_int64 0
81
+ else
82
+ raise "CF.CFNumberGetValue failed to convert #{self.inspect} to kCFNumberSInt64Type"
83
+ end
84
+ end
85
+
86
+ # Converts the CF::Number to a float
87
+ # May raise if the conversion cannot be done without loss of precision
88
+ # @return [Float]
89
+ def to_f
90
+ p = FFI::MemoryPointer.new(:double)
91
+ if CF.CFNumberGetValue(self, :kCFNumberDoubleType, p) != 0
92
+ p.read_double
93
+ else
94
+ raise "CF.CFNumberGetValue failed to convert #{self.inspect} to kCFNumberDoubleType"
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,77 @@
1
+ module CF
2
+ typedef :pointer, :cfstringref
3
+
4
+ attach_function 'CFStringCreateWithBytes', [:pointer, :buffer_in, :cfindex, :uint, :char], :cfstringref
5
+ attach_function 'CFStringGetBytes', [:cfstringref, :uint], :pointer
6
+ attach_function 'CFStringGetMaximumSizeForEncoding', [:cfindex, :uint], :cfindex
7
+ attach_function 'CFStringGetLength', [:cfstringref], :cfindex
8
+
9
+ attach_function 'CFStringGetBytes', [:cfstringref, CF::Range.by_value, :uint, :uchar, :char, :buffer_out, :cfindex, :buffer_out], :cfindex
10
+
11
+ attach_function 'CFStringCompare', [:cfstringref, :cfstringref, :cfoptionflags], :cfcomparisonresult
12
+
13
+ # Wrapper class for CFString
14
+ #
15
+ # Unlike ruby, CFString is not an arbitrary bag of bytes - the data will be converted to a collection of unicode characters
16
+ class String < Base
17
+ include Comparable
18
+ register_type("CFString")
19
+
20
+ # The cfstring encoding for UTF8
21
+ UTF8 = 0x08000100 #From cfstring.h
22
+
23
+
24
+ # Creates a string from a ruby string
25
+ # The string must be convertable to UTF-8
26
+ #
27
+ # @param [String] s
28
+ # @return [CF::String]
29
+ def self.from_string(s)
30
+ s_utf = s.encode('UTF-8')
31
+ raw = CF.CFStringCreateWithBytes(nil, s_utf, s_utf.bytesize, UTF8, 0)
32
+ raw.null? ? nil : new(raw).release_on_gc
33
+ end
34
+
35
+ # Returns the length, in unicode characters of the string
36
+ # @return [Integer]
37
+ def length
38
+ CF.CFStringGetLength(self)
39
+ end
40
+
41
+ # Compares the receiver with the argument
42
+ # @param [CF::String] other
43
+ # @return [Integer]
44
+ def <=>(other)
45
+ Base.check_cftype(other)
46
+ CF.CFStringCompare(self,other,0)
47
+ end
48
+
49
+ # Converts the CF::String to a UTF-8 ruby string
50
+ #
51
+ # @return [String]
52
+ def to_s
53
+ max_size = CF.CFStringGetMaximumSizeForEncoding(length, UTF8)
54
+ range = CF::Range.new
55
+ range[:location] = 0
56
+ range[:length] = length
57
+ buffer = FFI::MemoryPointer.new(:char, max_size)
58
+
59
+ cfindex = CF.find_type(:cfindex)
60
+ bytes_used_buffer = FFI::MemoryPointer.new(cfindex)
61
+
62
+ CF.CFStringGetBytes(self, range, UTF8, 0, 0, buffer, max_size, bytes_used_buffer)
63
+
64
+ bytes_used = if cfindex == CF.find_type(:long_long)
65
+ bytes_used_buffer.read_long_long
66
+ else
67
+ bytes_used_buffer.read_long
68
+ end
69
+
70
+ buffer.read_string(bytes_used).force_encoding(Encoding::UTF_8)
71
+ end
72
+
73
+ alias_method :to_ruby, :to_s
74
+
75
+ end
76
+
77
+ end
@@ -0,0 +1,4 @@
1
+ module CF
2
+ # The current version string
3
+ VERSION = "0.1.4"
4
+ end
@@ -0,0 +1,92 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF::Array do
4
+ describe 'mutable' do
5
+ subject { CF::Array.mutable}
6
+
7
+ it { should be_a(CF::Array)}
8
+ it { should be_mutable}
9
+
10
+ describe '[]=' do
11
+ it 'should raise when trying to store a non cf value' do
12
+ expect {subject[0] = 123}.to raise_error(TypeError)
13
+ end
14
+ end
15
+
16
+ describe '<<' do
17
+ it 'should raise when trying to store a non cf value' do
18
+ expect {subject << 123}.to raise_error(TypeError)
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ describe 'immutable' do
25
+ it 'should raise if all of the array elements are not cf values' do
26
+ expect {CF::Array.immutable([CF::Boolean::TRUE, 1])}.to raise_error(TypeError)
27
+ end
28
+
29
+ it 'should return an immutable cfarray' do
30
+ CF::Array.immutable([CF::Boolean::TRUE]).should be_a(CF::Array)
31
+ end
32
+
33
+ context 'with an immutable array' do
34
+ subject { CF::Array.immutable([CF::Boolean::TRUE, CF::String.from_string('123')])}
35
+
36
+ describe '[]=' do
37
+ it 'should raise TypeError' do
38
+ expect {subject[0] = CF::Boolean::TRUE}.to raise_error(TypeError)
39
+ end
40
+ end
41
+
42
+ describe '<<' do
43
+ it 'should raise TypeError' do
44
+ expect {subject << CF::Boolean::TRUE}.to raise_error(TypeError)
45
+ end
46
+ end
47
+ end
48
+ end
49
+
50
+ context "with an array" do
51
+ subject { CF::Array.immutable([CF::Boolean::TRUE, CF::String.from_string('123')])}
52
+
53
+ describe '[]' do
54
+ it 'should return the typecast value at the index' do
55
+ subject[1].should be_a(CF::String)
56
+ subject[1].should == CF::String.from_string('123')
57
+ end
58
+ end
59
+
60
+
61
+ describe 'length' do
62
+ it 'should return the count of items in the dictionary' do
63
+ subject.length.should == 2
64
+ end
65
+ end
66
+
67
+ describe 'to_ruby' do
68
+ it 'should return the result of calling to ruby on its contents' do
69
+ subject.to_ruby.should == [true, '123']
70
+ end
71
+ end
72
+
73
+ describe 'each' do
74
+ it 'should iterate over each value' do
75
+ values = []
76
+ subject.each do |v|
77
+ values << v
78
+ end
79
+ values[0].should == CF::Boolean::TRUE
80
+ values[1].should == CF::String.from_string('123')
81
+ end
82
+ end
83
+
84
+ it 'should be enumerable' do
85
+ values = {}
86
+ subject.each_with_index do |value, index|
87
+ values[index] = value
88
+ end
89
+ values.should == {0 => CF::Boolean::TRUE, 1 => CF::String.from_string('123')}
90
+ end
91
+ end
92
+ end