corefoundation 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -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