og-corefoundation 0.2.1

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