og-corefoundation 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,158 @@
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 {#binary?} returns true a {CF::Data} is returned, if not a {CF::String} is returned
50
+ #
51
+ # If you want a {CF::Data} with the contents of a non binary string, use {#to_cf_data}
52
+ #
53
+ # @return [CF::String, CF::Data]
54
+ def to_cf
55
+ self.binary? ? self.to_cf_data : self.to_cf_string
56
+ end
57
+
58
+ # @!method binary?
59
+ #
60
+ # used to determine whether {#to_cf} should return a {CF::String} or a {CF::Data}. On ruby 1.9 and above this simply
61
+ # checks whether the encoding is ascii-8bit or not.
62
+ #
63
+ # On ruby 1.8.7
64
+ #
65
+ # - A string is binary if you call {#binary!} with the default argument of true
66
+ # - A string is not binary if you call {#binary!} with the argument false
67
+ #
68
+ # If you have never called {#binary!} then a string is binary if Iconv does not think it is valid utf-8
69
+ # @return whether the string is handled as binary data or not
70
+ #
71
+ if '<3'.respond_to? :encoding
72
+ def binary?
73
+ encoding == Encoding::ASCII_8BIT
74
+ end
75
+ else
76
+ def binary?
77
+ unless defined? @cf_is_binary
78
+ begin
79
+ ::Iconv.conv('UTF-8', 'UTF-8', self)
80
+ return false if self.frozen?
81
+ @cf_is_binary = false
82
+ rescue Iconv::IllegalSequence
83
+ return true if self.frozen?
84
+ @cf_is_binary = true
85
+ end
86
+ end
87
+ @cf_is_binary
88
+ end
89
+ end
90
+
91
+ if '<3'.respond_to? :encoding
92
+ # On ruby 1.8.7 sets or clears the flag used by {#binary?}. On ruby 1.9 the string's encoding is forced.
93
+ # @see #binary?
94
+ #
95
+ # @note There is no advantage to using this over the standard encoding methods unless you wish to retain 1.8.7 compatibility
96
+ #
97
+ # @param [optional, Boolean, Encoding] bin On ruby 1.8.7 only boolean values are admissible. On ruby 1.9 you can pass a specific encoding to force.
98
+ # If you pass `true` then `Encoding::ASCII_BIT` is used, if you pass `false` then `Encoding::UTF_8`
99
+ #
100
+ def binary!(bin=true)
101
+ if bin == true
102
+ self.force_encoding Encoding::ASCII_8BIT
103
+ else
104
+ # default to utf-8
105
+ self.force_encoding( (bin == false) ? "UTF-8" : bin)
106
+ end
107
+ self
108
+ end
109
+ else
110
+ # On ruby 1.8.7 sets or clears the flag used by {#binary?}. On ruby 1.9 the string's encoding is forced.
111
+ # @see #binary?
112
+ #
113
+ # @note There is no advantage to using this over the standard encoding methods unless you wish to retain 1.8.7 compatibility
114
+ #
115
+ # @param [optional, Boolean, Encoding] bin On ruby 1.8.7 only boolean values are admissible. On ruby 1.9 you can pass a specific encoding to force.
116
+ # If you pass `true` then `Encoding::ASCII_BIT` is used, if you pass `false` then `Encoding::UTF_8`
117
+ #
118
+ def binary!(bin=true)
119
+ @cf_is_binary = !! bin
120
+ self
121
+ end
122
+ end
123
+
124
+ # Returns a {CF::String} representing the string
125
+ # @return [CF::String]
126
+ def to_cf_string
127
+ CF::String.from_string self
128
+ end
129
+
130
+ # Returns a {CF::Data} representing the string
131
+ # @return [CF::Data]
132
+ def to_cf_data
133
+ CF::Data.from_string self
134
+ end
135
+
136
+ end
137
+
138
+ # Ruby Time class
139
+ class Time
140
+ # Returns a {CF::Date} representing the time.
141
+ # @return [CF::Date]
142
+ def to_cf
143
+ CF::Date.from_time(self)
144
+ end
145
+ end
146
+
147
+ # Ruby Hash class
148
+ class Hash
149
+ # Converts the Hash to an mutable {CF::Dictionary} by calling `to_cf` on each key and value it contains
150
+ # @return [CF::Dictionary]
151
+ def to_cf
152
+ CF::Dictionary.mutable.tap do |r|
153
+ each do |k,v|
154
+ r[k.to_cf] = v.to_cf
155
+ end
156
+ end
157
+ end
158
+ 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,91 @@
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
+ # workaround for ruby 1.8.7 compat
24
+ HAS_ENCODING = "foo".respond_to? "encode"
25
+
26
+ # Creates a string from a ruby string
27
+ # The string must be convertable to UTF-8
28
+ #
29
+ # @param [String] s
30
+ # @return [CF::String]
31
+ def self.from_string(s, src_encoding='UTF-8')
32
+ if HAS_ENCODING
33
+ s_utf = s.encode('UTF-8')
34
+ else
35
+ begin
36
+ s_utf = Iconv.conv('UTF-8', src_encoding, s.to_s)
37
+ rescue Iconv::IllegalSequence => e
38
+ return nil
39
+ end
40
+ end
41
+ raw = CF.CFStringCreateWithBytes(nil, s_utf, s_utf.bytesize, UTF8, 0)
42
+ raw.null? ? nil : new(raw).release_on_gc
43
+ end
44
+
45
+ # Returns the length, in unicode characters of the string
46
+ # @return [Integer]
47
+ def length
48
+ CF.CFStringGetLength(self)
49
+ end
50
+
51
+ # Compares the receiver with the argument
52
+ # @param [CF::String] other
53
+ # @return [Integer]
54
+ def <=>(other)
55
+ Base.check_cftype(other)
56
+ CF.CFStringCompare(self,other,0)
57
+ end
58
+
59
+ # Converts the CF::String to a UTF-8 ruby string
60
+ #
61
+ # @return [String]
62
+ def to_s
63
+ max_size = CF.CFStringGetMaximumSizeForEncoding(length, UTF8)
64
+ range = CF::Range.new
65
+ range[:location] = 0
66
+ range[:length] = length
67
+ buffer = FFI::MemoryPointer.new(:char, max_size)
68
+
69
+ cfindex = CF.find_type(:cfindex)
70
+ bytes_used_buffer = FFI::MemoryPointer.new(cfindex)
71
+
72
+ CF.CFStringGetBytes(self, range, UTF8, 0, 0, buffer, max_size, bytes_used_buffer)
73
+
74
+ bytes_used = if cfindex == CF.find_type(:long_long)
75
+ bytes_used_buffer.read_long_long
76
+ else
77
+ bytes_used_buffer.read_long
78
+ end
79
+
80
+ if HAS_ENCODING
81
+ buffer.read_string(bytes_used).force_encoding(Encoding::UTF_8)
82
+ else
83
+ buffer.read_string(bytes_used)
84
+ end
85
+ end
86
+
87
+ alias_method :to_ruby, :to_s
88
+
89
+ end
90
+
91
+ end
@@ -0,0 +1,4 @@
1
+ module CF
2
+ # The current version string
3
+ VERSION = "0.2.1"
4
+ end
@@ -0,0 +1,13 @@
1
+ require 'ffi'
2
+ require 'iconv' if RUBY_VERSION < "1.9"
3
+
4
+ require 'corefoundation/base'
5
+ require 'corefoundation/null'
6
+ require 'corefoundation/string'
7
+ require 'corefoundation/array'
8
+ require 'corefoundation/boolean'
9
+ require 'corefoundation/data'
10
+ require 'corefoundation/dictionary'
11
+ require 'corefoundation/number'
12
+ require 'corefoundation/date'
13
+ require 'corefoundation/extensions'
@@ -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
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF do
4
+
5
+
6
+ describe CF::Boolean do
7
+ describe 'value' do
8
+ it 'should return true for CF::Boolean::TRUE' do
9
+ CF::Boolean::TRUE.value.should == true
10
+ end
11
+ it 'should return false for CF::Boolean::FALSE' do
12
+ CF::Boolean::FALSE.value.should == false
13
+ end
14
+ end
15
+
16
+ describe 'to_ruby' do
17
+ it 'should behave like value' do
18
+ CF::Boolean::FALSE.to_ruby.should == false
19
+ end
20
+ end
21
+
22
+ end
23
+
24
+ end
data/spec/data_spec.rb ADDED
@@ -0,0 +1,30 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe CF::Data do
5
+ subject {CF::Data.from_string('A CF string')}
6
+ describe '#to_s' do
7
+ it 'should return a binary ruby string' do
8
+ ruby_string = subject.to_s
9
+ ruby_string.should == 'A CF string'
10
+ if CF::String::HAS_ENCODING
11
+ ruby_string.encoding.should == Encoding::ASCII_8BIT
12
+ end
13
+ end
14
+ end
15
+
16
+ describe '#size' do
17
+ it 'should return the size in bytes of the cfdata' do
18
+ subject.size.should == 11
19
+ end
20
+ end
21
+
22
+ describe 'to_ruby' do
23
+ it 'should behave like to_s' do
24
+ subject.to_ruby.should == 'A CF string'
25
+ if 'A CF string'.respond_to? "encoding"
26
+ subject.to_ruby.encoding.should == Encoding::ASCII_8BIT
27
+ end
28
+ end
29
+ end
30
+ end
data/spec/date_spec.rb ADDED
@@ -0,0 +1,25 @@
1
+ require 'spec_helper'
2
+
3
+ describe CF::Date do
4
+ describe('from_time') do
5
+ it 'should create a cf date from a time' do
6
+ CF::Date.from_time(Time.now).should be_a(CF::Date)
7
+ end
8
+ end
9
+
10
+ describe('to_time') do
11
+ it 'should return a time' do
12
+ t = CF::Date.from_time(Time.now).to_time
13
+ t.should be_a(Time)
14
+ t.should be_within(0.01).of(Time.now)
15
+ end
16
+ end
17
+
18
+ describe 'to_ruby' do
19
+ it 'should behave like to_time' do
20
+ t = CF::Date.from_time(Time.now).to_ruby
21
+ t.should be_a(Time)
22
+ end
23
+ end
24
+ end
25
+
@@ -0,0 +1,81 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe CF::Dictionary do
5
+ describe 'mutable' do
6
+ it 'should return a cf dictionary' do
7
+ CF::Dictionary.mutable.should be_a(CF::Dictionary)
8
+ end
9
+ end
10
+
11
+ describe 'hash access' do
12
+ subject {CF::Dictionary.mutable}
13
+
14
+ it 'should return nil when the key does not exist' do
15
+ subject['doesnotexist'].should be_nil
16
+ end
17
+
18
+ it 'should raise when trying to store a non cf value' do
19
+ expect {subject[CF::String.from_string('key')]=1}.to raise_error(TypeError)
20
+ end
21
+
22
+ it 'should raise when trying to store a non cf key' do
23
+ expect {subject[1]=CF::String.from_string('value')}.to raise_error(TypeError)
24
+ end
25
+
26
+ it 'should allow storing and retrieving a cf key pair' do
27
+ subject[CF::String.from_string('key')] = CF::String.from_string('value')
28
+ end
29
+
30
+ it 'should instantiate the correct type on retrieval' do
31
+ subject[CF::String.from_string('key')] = CF::String.from_string('value')
32
+ subject[CF::String.from_string('key')].should be_a(CF::String)
33
+ end
34
+
35
+ it 'should coerce string keys' do
36
+ subject['key'] = CF::String.from_string('value')
37
+ subject['key'].to_s.should == 'value'
38
+ end
39
+ end
40
+
41
+ describe 'merge!' do
42
+ subject { CF::Dictionary.mutable.tap {|dict| dict['1'] = CF::Boolean::TRUE; dict['2'] = CF::Boolean::FALSE}}
43
+ it 'should merge the argument into the receiver' do
44
+ argument = {'1' => false, 'foo' => 'bar'}.to_cf
45
+ subject.merge! argument
46
+ subject.to_ruby.should == {'1' => false, '2' => false, 'foo' => 'bar'}
47
+ end
48
+ end
49
+
50
+ describe 'length' do
51
+ it 'should return the count of items in the dictionary' do
52
+ dict = CF::Dictionary.mutable
53
+ dict['one'] = CF::Boolean::TRUE
54
+ dict['two'] = CF::Boolean::TRUE
55
+
56
+ dict.length.should == 2
57
+ end
58
+ end
59
+
60
+ describe 'enumeration' do
61
+ subject { CF::Dictionary.mutable.tap {|dict| dict['1'] = CF::Boolean::TRUE; dict['2'] = CF::Boolean::FALSE}}
62
+
63
+ it 'should yield each key value pair in the dictionary' do
64
+ hash = {}
65
+ subject.each do |k,v|
66
+ hash[k] = v
67
+ end
68
+ hash.should == {CF::String.from_string('1') => CF::Boolean::TRUE,
69
+ CF::String.from_string('2') => CF::Boolean::FALSE}
70
+ end
71
+ end
72
+
73
+ describe 'to_ruby' do
74
+ subject { CF::Dictionary.mutable.tap {|dict| dict['1'] = CF::Boolean::TRUE; dict['2'] = CF::Array.immutable([CF::Boolean::FALSE])}}
75
+
76
+ it 'should return a ruby hash where keys and values have been converted to ruby types' do
77
+ subject.to_ruby.should == {'1' => true, '2' => [false]}
78
+ end
79
+ end
80
+
81
+ end