motion-addressbook 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (31) hide show
  1. data/.travis.yml +0 -1
  2. data/README.md +2 -3
  3. data/Rakefile +16 -4
  4. data/lib/motion-addressbook/version.rb +1 -1
  5. data/lib/motion-addressbook.rb +22 -10
  6. data/motion/address_book/{addr_book.rb → ios/addr_book.rb} +0 -0
  7. data/motion/address_book/{group.rb → ios/group.rb} +0 -0
  8. data/motion/address_book/{multi_value.rb → ios/multi_value.rb} +0 -0
  9. data/motion/address_book/{multi_valued.rb → ios/multi_valued.rb} +0 -0
  10. data/motion/address_book/{person.rb → ios/person.rb} +8 -0
  11. data/motion/address_book/{picker.rb → ios/picker.rb} +0 -0
  12. data/motion/address_book/{source.rb → ios/source.rb} +0 -0
  13. data/motion/address_book/osx/addr_book.rb +67 -0
  14. data/motion/address_book/osx/group.rb +122 -0
  15. data/motion/address_book/osx/multi_valued.rb +166 -0
  16. data/motion/address_book/osx/person.rb +350 -0
  17. data/motion/address_book.rb +13 -5
  18. data/motion-addressbook.gemspec +2 -2
  19. data/spec/{address_book → ios/address_book}/group_spec.rb +0 -0
  20. data/spec/{address_book → ios/address_book}/multi_valued_spec.rb +0 -0
  21. data/spec/{address_book → ios/address_book}/person_spec.rb +33 -154
  22. data/spec/{address_book → ios/address_book}/picker_spec.rb +0 -0
  23. data/spec/{helpers → ios/helpers}/bacon_matchers.rb +0 -0
  24. data/spec/{helpers → ios/helpers}/hacks.rb +0 -0
  25. data/spec/{helpers → ios/helpers}/person_helpers.rb +0 -0
  26. data/spec/osx/address_book/person_spec.rb +174 -0
  27. data/spec/osx/helpers/bacon_matchers.rb +11 -0
  28. data/spec/osx/helpers/hacks.rb +6 -0
  29. data/spec/osx/helpers/person_helpers.rb +12 -0
  30. metadata +39 -29
  31. data/spec/address_book/multi_value_spec.rb +0 -125
@@ -0,0 +1,350 @@
1
+ module AddressBook
2
+ class Person
3
+ attr_reader :error
4
+
5
+ def initialize(target, opts = {})
6
+ @address_book = opts[:address_book]
7
+ if target.respond_to?(:fetch)
8
+ # data for a new Person, to be saved to the Address Book
9
+ @ab_person = nil
10
+ @attributes = target
11
+ else
12
+ # existing Person, to be retrieved from the OSX Address Book
13
+ @ab_person = target
14
+ @attributes = nil
15
+ end
16
+ end
17
+
18
+ def save
19
+ address_book.addRecord(ab_person)
20
+ address_book.save
21
+ @attributes = nil # force refresh
22
+ uid
23
+ end
24
+
25
+ def attributes
26
+ @attributes || import_ab_person
27
+ end
28
+
29
+ def ab_person
30
+ @ab_person ||= initialize_ab_person
31
+ end
32
+ alias :ab_record :ab_person
33
+
34
+ def uid
35
+ get_field(KABUIDProperty)
36
+ end
37
+
38
+ # this is NOT the same as the uid
39
+ # this may be assigned when iCloud syncing is enabled
40
+ def apple_uuid
41
+ get_field('com.apple.uuid')
42
+ end
43
+
44
+ def method_missing(name, *args)
45
+ if property = ReverseSingleValuePropertyMap[name]
46
+ get_field(property)
47
+ else
48
+ super
49
+ end
50
+ end
51
+ # def self.method_missing(name, *args)
52
+ # if attribute_name = all_finder?(name)
53
+ # find_all_by(attribute_name, args.first)
54
+ # elsif attribute_name = first_finder?(name)
55
+ # find_by(attribute_name, args.first)
56
+ # elsif attribute_name = finder_or_new?(name)
57
+ # find_or_new_by(attribute_name, args.first)
58
+ # else
59
+ # super
60
+ # end
61
+ # end
62
+ # def self.is_attribute?(attribute_name)
63
+ # return false if attribute_name.nil?
64
+ # attribute_map.include?(attribute_name.to_sym) || [:email, :phone_number].include?( attribute_name.to_sym)
65
+ # end
66
+
67
+ # def getter?(method_name)
68
+ # if self.class.is_attribute? method_name
69
+ # method_name
70
+ # # true
71
+ # else
72
+ # nil
73
+ # end
74
+ # end
75
+ # def setter?(method_name)
76
+ # method_name.to_s =~ /^(\w*)=$/
77
+ # if self.class.is_attribute? $1
78
+ # $1
79
+ # else
80
+ # nil
81
+ # end
82
+ # end
83
+
84
+ def get(attribute_name)
85
+ # label, attribute = attribute_name.split('_')
86
+ # self.send("#{attribute}s").first_for(label)
87
+ attributes[attribute_name.to_sym] ||= get_field(attribute_map[attribute_name])
88
+ end
89
+
90
+ def set(attribute_name, value)
91
+ set_field(attribute_map[attribute_name.to_sym], value)
92
+ attributes[attribute_name.to_sym] = value
93
+ end
94
+
95
+ def photo_image
96
+ UIImage.alloc.initWithData(photo)
97
+ end
98
+
99
+ def photo
100
+ ab_person.imageData
101
+ end
102
+
103
+ def photo=(photo_data)
104
+ ab_person.setImageData(photo_data)
105
+ end
106
+
107
+ def get_multi_valued(field)
108
+ if mv = get_field(field)
109
+ MultiValued.new(:ab_multi_value => mv)
110
+ end
111
+ end
112
+
113
+ def phones
114
+ get_multi_valued(KABPhoneProperty)
115
+ end
116
+
117
+ def phone_values
118
+ phones.attributes.map {|r| r[:value]}
119
+ end
120
+
121
+ def emails
122
+ get_multi_valued(KABEmailProperty)
123
+ end
124
+
125
+ def email_values
126
+ emails.attributes.map {|r| r[:value]}
127
+ end
128
+
129
+ def addresses
130
+ get_multi_valued(KABAddressProperty)
131
+ end
132
+
133
+ def urls
134
+ get_multi_valued(KABURLsProperty)
135
+ end
136
+
137
+ def social_profiles
138
+ get_multi_valued(KABSocialProfileProperty)
139
+ end
140
+
141
+ def im_profiles
142
+ get_multi_valued(KABInstantMessageProperty)
143
+ end
144
+
145
+ def related_names
146
+ get_multi_valued(KABRelatedNamesProperty)
147
+ end
148
+
149
+ def dates
150
+ get_multi_valued(KABDateProperty)
151
+ end
152
+
153
+ def email; email_values.first; end
154
+ def phone; phone_values.first; end
155
+ def url; urls.attributes.first[:value]; end
156
+ def address; addresses.attributes.first; end
157
+
158
+ # def find_or_new
159
+ # if new_record?
160
+ # new_ab_person
161
+ # else
162
+ # existing_record
163
+ # end
164
+ # end
165
+
166
+ # has this record already been saved to the address book?
167
+ def exists?
168
+ uid && ABAddressBook.sharedAddressBook.recordForUniqueId(uid)
169
+ end
170
+ def new_record?
171
+ !exists?
172
+ end
173
+ alias :new? :new_record?
174
+
175
+ def delete!
176
+ if exists?
177
+ address_book.removeRecord(ab_person)
178
+ address_book.save
179
+ @ab_person = nil
180
+ self
181
+ end
182
+ end
183
+
184
+ # not supported on OSX
185
+ # def composite_name
186
+ # ABRecordCopyCompositeName(ab_person)
187
+ # end
188
+
189
+ def person?
190
+ (get_field(KABPersonFlags) & KABShowAsCompany == 0)
191
+ end
192
+ def organization?
193
+ (get_field(KABPersonFlags) & KABShowAsCompany == 1)
194
+ end
195
+
196
+ def modification_date
197
+ get_field(KABModificationDateProperty)
198
+ end
199
+
200
+ def creation_date
201
+ get_field(KABCreationDateProperty)
202
+ end
203
+
204
+ # replace *all* properties of an existing Person with new values
205
+ def replace(new_attributes)
206
+ @attributes = new_attributes
207
+ load_ab_person
208
+ end
209
+
210
+ def linked_people
211
+ recs = ab_person.linkedPeople
212
+ recs.delete(ab_person) # LinkedPeople always includes self
213
+ recs.map do |linked_rec|
214
+ Person.new(nil, linked_rec, :address_book => address_book)
215
+ end
216
+ end
217
+
218
+ def to_vcard
219
+ ab_person.vCardRepresentation
220
+ end
221
+
222
+ def groups
223
+ ab_person.parentGroups.map do |ab_group|
224
+ AddressBook::Group.new(:ab_group => ab_group, :address_book => address_book)
225
+ end
226
+ end
227
+
228
+ def local?
229
+ !ab_person.isReadOnly
230
+ end
231
+
232
+ def to_s
233
+ "#<#{self.class}:#{uid}: #{attributes}>"
234
+ end
235
+ alias :inspect :to_s
236
+
237
+ # private
238
+
239
+ SingleValuePropertyMap = {
240
+ KABFirstNameProperty => :first_name,
241
+ KABLastNameProperty => :last_name,
242
+ KABMiddleNameProperty => :middle_name,
243
+ KABPrefixProperty => :prefix,
244
+ KABSuffixProperty => :suffix,
245
+ KABNicknameProperty => :nickname,
246
+ KABJobTitleProperty => :job_title,
247
+ KABDepartmentProperty => :department,
248
+ KABOrganizationProperty => :organization,
249
+ KABBirthdayProperty => :birthday,
250
+ KABNoteProperty => :note
251
+ }
252
+ ReverseSingleValuePropertyMap = SingleValuePropertyMap.invert
253
+
254
+ MultiValuePropertyMap = {
255
+ KABPhoneProperty => :phones,
256
+ KABEmailProperty => :emails,
257
+ KABAddressProperty => :addresses,
258
+ KABURLsProperty => :urls,
259
+ KABSocialProfileProperty => :social_profiles,
260
+ KABInstantMessageProperty => :im_profiles,
261
+ KABRelatedNamesProperty => :related_names,
262
+ KABOtherDatesProperty => :dates
263
+ }
264
+
265
+ # instantiates ABPerson record from attributes
266
+ def initialize_ab_person
267
+ @ab_person = ABPerson.alloc.initWithAddressBook(address_book)
268
+ load_ab_person
269
+ @ab_person
270
+ end
271
+
272
+ def load_ab_person
273
+ @attributes ||= {}
274
+
275
+ SingleValuePropertyMap.each do |ab_property, attr_key|
276
+ if attributes[attr_key]
277
+ set_field(ab_property, attributes[attr_key])
278
+ else
279
+ remove_field(ab_property)
280
+ end
281
+ end
282
+
283
+ if attributes[:is_org]
284
+ set_field(KABPersonFlags, KABShowAsCompany)
285
+ else
286
+ set_field(KABPersonFlags, KABShowAsPerson)
287
+ end
288
+
289
+ MultiValuePropertyMap.each do |ab_property, attr_key|
290
+ if attributes[attr_key]
291
+ set_multi_valued(ab_property, attributes[attr_key])
292
+ else
293
+ remove_field(ab_property)
294
+ end
295
+ end
296
+
297
+ ab_person
298
+ end
299
+
300
+ # populate attributes from existing ABPerson
301
+ def import_ab_person
302
+ @attributes = {}
303
+ @modification_date = nil
304
+
305
+ SingleValuePropertyMap.each do |ab_property, attr_key|
306
+ if value = get_field(ab_property)
307
+ @attributes[attr_key] = value
308
+ end
309
+ end
310
+
311
+ if organization?
312
+ @attributes[:is_org] = true
313
+ end
314
+
315
+ MultiValuePropertyMap.each do |ab_property, attr_key|
316
+ if value = get_multi_valued(ab_property)
317
+ if value.attributes.any?
318
+ @attributes[attr_key] = value.attributes
319
+ end
320
+ end
321
+ end
322
+
323
+ @attributes
324
+ end
325
+
326
+ def set_field(field, value)
327
+ if value
328
+ ab_person.setValue(value, forProperty:field)
329
+ end
330
+ end
331
+ def get_field(field)
332
+ ab_person.valueForProperty(field)
333
+ end
334
+ def remove_field(field)
335
+ ab_person.removeValueForProperty(field)
336
+ end
337
+
338
+ def set_multi_valued(field, values)
339
+ values = values.map { |value| ( (value.kind_of?String) ? {:value => value} : value)}
340
+ if values && values.any?
341
+ multi_field = MultiValued.new(:attributes => values)
342
+ set_field(field, multi_field.ab_multi_value)
343
+ end
344
+ end
345
+
346
+ def address_book
347
+ @address_book ||= AddressBook.address_book
348
+ end
349
+ end
350
+ end
@@ -2,15 +2,23 @@ module AddressBook
2
2
  module_function
3
3
 
4
4
  def address_book
5
- if UIDevice.currentDevice.systemVersion >= '6'
6
- ios6_create
7
- else
8
- ios5_create
5
+ if App.osx?
6
+ ABAddressBook.addressBook
7
+ else # iOS
8
+ if Device.ios_version == '6.0'
9
+ ios6_create
10
+ else
11
+ ios5_create
12
+ end
9
13
  end
10
14
  end
11
15
 
12
16
  def count
13
- ABAddressBookGetPersonCount(address_book)
17
+ if App.ios?
18
+ ABAddressBookGetPersonCount(address_book)
19
+ else
20
+ address_book.count
21
+ end
14
22
  end
15
23
 
16
24
  def ios6_create
@@ -7,8 +7,8 @@ Gem::Specification.new do |gem|
7
7
 
8
8
  gem.authors = ["Alex Rothenberg", "Jason May"]
9
9
  gem.email = ["alex@alexrothenberg.com", "jmay@pobox.com"]
10
- gem.description = %q{A RubyMotion wrapper around the iOS Address Book framework}
11
- gem.summary = %q{A RubyMotion wrapper around the iOS Address Book framework}
10
+ gem.description = %q{A RubyMotion wrapper around the iOS & OSX Address Book frameworks}
11
+ gem.summary = %q{A RubyMotion wrapper around the iOS & OSX Address Book frameworks}
12
12
  gem.homepage = ""
13
13
 
14
14
  gem.files = `git ls-files`.split($\)