motion-addressbook 1.2.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -1
- data/.travis.yml +6 -5
- data/Gemfile +1 -2
- data/README.md +18 -4
- data/Rakefile +6 -1
- data/abhack/abhack.h +8 -0
- data/abhack/abhack.m +15 -0
- data/lib/motion-addressbook/version.rb +1 -1
- data/lib/motion-addressbook.rb +6 -0
- data/motion/address_book/addr_book.rb +17 -3
- data/motion/address_book/group.rb +3 -2
- data/motion/address_book/multi_valued.rb +88 -19
- data/motion/address_book/person.rb +102 -21
- data/motion/address_book/source.rb +17 -0
- data/motion-addressbook.gemspec +9 -7
- data/spec/address_book/group_spec.rb +5 -0
- data/spec/address_book/multi_valued_spec.rb +107 -2
- data/spec/address_book/person_spec.rb +125 -6
- metadata +13 -8
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
language: objective-c
|
2
|
-
before_install:
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
before_install: rvm use 1.9.3
|
3
|
+
notifications:
|
4
|
+
email:
|
5
|
+
recipients:
|
6
|
+
- alex@alexrothenberg.com
|
7
|
+
- jmay@pobox.com
|
data/Gemfile
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
source 'https://rubygems.org'
|
2
2
|
|
3
3
|
# need to do this here as alphabetically "bubble-wrap" comes after "address_book" and require fails otherwise
|
4
|
-
gem 'bubble-wrap'
|
4
|
+
gem 'bubble-wrap', '~> 1.3'
|
5
5
|
|
6
6
|
# Specify your gem's dependencies in motion-addressbook.gemspec
|
7
7
|
gemspec
|
8
|
-
|
data/README.md
CHANGED
@@ -8,7 +8,14 @@ Apple's [Address Book Programming Guide for iOS](http://developer.apple.com/libr
|
|
8
8
|
|
9
9
|
## Installation
|
10
10
|
|
11
|
-
|
11
|
+
### If you're using `bundler` (this is recommended):
|
12
|
+
|
13
|
+
Add these lines to your application's `Rakefile`:
|
14
|
+
|
15
|
+
require 'bundler'
|
16
|
+
Bundler.require
|
17
|
+
|
18
|
+
Add this line to your application's `Gemfile`:
|
12
19
|
|
13
20
|
gem 'motion-addressbook'
|
14
21
|
|
@@ -16,8 +23,11 @@ And then execute:
|
|
16
23
|
|
17
24
|
$ bundle
|
18
25
|
|
19
|
-
|
26
|
+
### Manually without bundler
|
27
|
+
|
28
|
+
Or install it yourself (remember to add the bubble-wrap dependency) as:
|
20
29
|
|
30
|
+
$ gem install bubble-wrap
|
21
31
|
$ gem install motion-addressbook
|
22
32
|
|
23
33
|
## Usage
|
@@ -130,10 +140,14 @@ AddressBook::Person.find_or_new_by_email('alex@example.com')
|
|
130
140
|
### Create a new Contact and save in Contacts app
|
131
141
|
|
132
142
|
```ruby
|
133
|
-
AddressBook::Person.create(:first_name => 'Alex', :last_name => 'Rothenberg', :email => 'alex@example.com')
|
143
|
+
AddressBook::Person.create(:first_name => 'Alex', :last_name => 'Rothenberg', :email => [{ :value => 'alex@example.com', :label => 'Home'}], , :phones => [{ :value => '9920149993', :label => 'Mobile'}])
|
134
144
|
# => #<AddressBook::Person:0xe4e3a80 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0xe4bbef0>>
|
135
|
-
```
|
136
145
|
|
146
|
+
# Multiple emails/phones ex.
|
147
|
+
|
148
|
+
AddressBook::Person.create(:first_name => 'Alex', :last_name => 'Rothenberg', :emails => ["a@mail.com", "b@gmail.com", "c@gmail.com", {:value => 'ashish@gmail.com', :label => 'Personal'} ], :phones => ['1234','2345','4567'])
|
149
|
+
=> #<AddressBook::Person:0x9ce23b0 @address_book=#<__NSCFType:0x9ce2660> @ab_person=#<__NSCFType:0x9ce2450> @attributes=nil>
|
150
|
+
```
|
137
151
|
### Update existing contact
|
138
152
|
|
139
153
|
```ruby
|
data/Rakefile
CHANGED
@@ -1,8 +1,13 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
2
|
$:.unshift("/Library/RubyMotion/lib")
|
3
|
-
require 'motion/project'
|
3
|
+
require 'motion/project/template/ios'
|
4
4
|
require "bundler/gem_tasks"
|
5
5
|
Bundler.setup
|
6
6
|
Bundler.require
|
7
7
|
|
8
8
|
require 'bubble-wrap/test'
|
9
|
+
|
10
|
+
Motion::Project::App.setup do |app|
|
11
|
+
# Use `rake config' to see complete project settings.
|
12
|
+
app.name = 'AddressBook'
|
13
|
+
end
|
data/abhack/abhack.h
ADDED
data/abhack/abhack.m
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#import "abhack.h"
|
2
|
+
|
3
|
+
@implementation ABHack
|
4
|
+
|
5
|
+
+ (NSDate *) getDateProperty: (ABPropertyID) property from: (ABRecordRef) ab_person
|
6
|
+
{
|
7
|
+
return (NSDate *)ABRecordCopyValue(ab_person, property);
|
8
|
+
}
|
9
|
+
|
10
|
+
+ (NSDate *) getDateValueAtIndex: (int) index from: (ABMultiValueRef) ab_multi_value
|
11
|
+
{
|
12
|
+
return (NSDate *)ABMultiValueCopyValueAtIndex(ab_multi_value, index);
|
13
|
+
}
|
14
|
+
|
15
|
+
@end
|
data/lib/motion-addressbook.rb
CHANGED
@@ -8,6 +8,12 @@ BW.require 'motion/address_book/addr_book.rb'
|
|
8
8
|
BW.require 'motion/address_book/person.rb'
|
9
9
|
BW.require 'motion/address_book/group.rb'
|
10
10
|
BW.require 'motion/address_book/multi_valued.rb'
|
11
|
+
BW.require 'motion/address_book/source.rb'
|
12
|
+
|
11
13
|
BW.require 'motion/address_book/picker.rb' do
|
12
14
|
file('motion/address_book/picker.rb').uses_framework('AddressBookUI')
|
13
15
|
end
|
16
|
+
|
17
|
+
Motion::Project::App.setup do |app|
|
18
|
+
app.vendor_project(File.expand_path(File.join(File.dirname(__FILE__), '../abhack')), :static)
|
19
|
+
end
|
@@ -5,9 +5,15 @@ module AddressBook
|
|
5
5
|
def initialize
|
6
6
|
@ab = AddressBook.address_book
|
7
7
|
end
|
8
|
-
def people
|
9
|
-
|
10
|
-
|
8
|
+
def people(opts = {})
|
9
|
+
if opts[:source]
|
10
|
+
ABAddressBookCopyArrayOfAllPeopleInSource(ab, opts[:source].ab_source).map do |ab_person|
|
11
|
+
AddressBook::Person.new({}, ab_person, :address_book => ab)
|
12
|
+
end
|
13
|
+
else
|
14
|
+
ABAddressBookCopyArrayOfAllPeople(ab).map do |ab_person|
|
15
|
+
AddressBook::Person.new({}, ab_person, :address_book => ab)
|
16
|
+
end
|
11
17
|
end
|
12
18
|
end
|
13
19
|
def count
|
@@ -24,6 +30,9 @@ module AddressBook
|
|
24
30
|
def person(id)
|
25
31
|
(p = ABAddressBookGetPersonWithRecordID(ab, id)) && Person.new(nil, p, :address_book => ab)
|
26
32
|
end
|
33
|
+
def changedSince(timestamp)
|
34
|
+
people.select {|p| p.modification_date > timestamp}
|
35
|
+
end
|
27
36
|
|
28
37
|
def groups
|
29
38
|
ABAddressBookCopyArrayOfAllGroups(@ab).map do |ab_group|
|
@@ -40,5 +49,10 @@ module AddressBook
|
|
40
49
|
def notify_changes(callback, context)
|
41
50
|
ABAddressBookRegisterExternalChangeCallback(ab, callback, context)
|
42
51
|
end
|
52
|
+
|
53
|
+
def sources
|
54
|
+
# ABAddressBookCopyArrayOfAllSources(ab).map {|s| ABRecordCopyValue(s, KABSourceTypeProperty)}
|
55
|
+
ABAddressBookCopyArrayOfAllSources(ab).map {|s| Source.new(s)}
|
56
|
+
end
|
43
57
|
end
|
44
58
|
end
|
@@ -32,9 +32,10 @@ module AddressBook
|
|
32
32
|
self
|
33
33
|
end
|
34
34
|
|
35
|
-
def
|
35
|
+
def new_record?
|
36
36
|
uid == KABRecordInvalidID
|
37
37
|
end
|
38
|
+
alias :new? :new_record?
|
38
39
|
|
39
40
|
def delete!
|
40
41
|
unless new?
|
@@ -77,7 +78,7 @@ module AddressBook
|
|
77
78
|
end
|
78
79
|
|
79
80
|
def <<(person_or_group)
|
80
|
-
raise "Must save member before adding to group" if person_or_group.new?
|
81
|
+
raise ArgumentError, "Must save member before adding to group" if person_or_group.new?
|
81
82
|
ABGroupAddMember(ab_group, person_or_group.ab_record, error)
|
82
83
|
end
|
83
84
|
|
@@ -1,15 +1,17 @@
|
|
1
1
|
module AddressBook
|
2
2
|
class MultiValued
|
3
|
+
attr_reader :mv_type
|
4
|
+
|
3
5
|
def initialize(opts)
|
4
6
|
unless opts.one?
|
5
7
|
raise ArgumentError, "MultiValued requires :attributes *or* :ab_multi_value argument"
|
6
8
|
end
|
7
9
|
|
8
10
|
if opts[:ab_multi_value]
|
9
|
-
|
10
|
-
@ab_multi_value = opts[:ab_multi_value]
|
11
|
+
@ab_multi_value = ABMultiValueCreateMutableCopy(opts[:ab_multi_value])
|
11
12
|
else
|
12
13
|
@attributes = opts[:attributes]
|
14
|
+
raise ArgumentError, "Empty multi-value objects are not allowed" if @attributes.empty?
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -26,7 +28,7 @@ module AddressBook
|
|
26
28
|
count.times.map do |i|
|
27
29
|
label = ABMultiValueCopyLabelAtIndex(@ab_multi_value, i)
|
28
30
|
label_val = ABAddressBookCopyLocalizedLabel(label)
|
29
|
-
data = ab_record_to_dict(
|
31
|
+
data = ab_record_to_dict(i)
|
30
32
|
data.merge(:label => label_val)
|
31
33
|
end
|
32
34
|
end
|
@@ -35,25 +37,51 @@ module AddressBook
|
|
35
37
|
@ab_multi_value ||= convert_dictionary_into_multi_value
|
36
38
|
end
|
37
39
|
|
40
|
+
def localized_label(str)
|
41
|
+
LabelMap[str] || str
|
42
|
+
end
|
43
|
+
|
38
44
|
def convert_dictionary_into_multi_value
|
39
|
-
|
40
|
-
|
45
|
+
@mv_type = multi_value_property_type
|
46
|
+
mv = ABMultiValueCreateMutable(mv_type)
|
47
|
+
|
48
|
+
case mv_type
|
49
|
+
when KABMultiStringPropertyType
|
41
50
|
@attributes.each do |rec|
|
42
|
-
ABMultiValueAddValueAndLabel(mv, rec[:value], rec[:label], nil)
|
51
|
+
ABMultiValueAddValueAndLabel(mv, rec[:value], localized_label(rec[:label]), nil)
|
43
52
|
end
|
44
|
-
|
45
|
-
|
46
|
-
|
53
|
+
when KABMultiDateTimePropertyType
|
54
|
+
@attributes.each do |rec|
|
55
|
+
ABMultiValueAddValueAndLabel(mv, rec[:date], localized_label(rec[:label]), nil)
|
56
|
+
end
|
57
|
+
else # KABMultiDictionaryPropertyType
|
47
58
|
@attributes.each do |rec|
|
48
|
-
|
59
|
+
if value = dict_to_ab_record(rec)
|
60
|
+
ABMultiValueAddValueAndLabel(mv, value, localized_label(rec[:label]), nil)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
mv
|
66
|
+
end
|
67
|
+
|
68
|
+
def multi_value_property_type
|
69
|
+
if @ab_multi_value
|
70
|
+
ABMultiValueGetPropertyType(@ab_multi_value)
|
71
|
+
else
|
72
|
+
if attributes.find {|rec| rec[:value]}
|
73
|
+
KABMultiStringPropertyType
|
74
|
+
elsif attributes.find {|rec| rec[:date]}
|
75
|
+
KABMultiDateTimePropertyType
|
76
|
+
else
|
77
|
+
KABMultiDictionaryPropertyType
|
49
78
|
end
|
50
|
-
mv
|
51
79
|
end
|
52
80
|
end
|
53
81
|
|
54
82
|
# these are for mapping fields in a kABMultiDictionaryPropertyType record
|
55
83
|
# to keys in a standard hash (NSDictionary)
|
56
|
-
|
84
|
+
PropertyMap = {
|
57
85
|
KABPersonAddressStreetKey => :street,
|
58
86
|
KABPersonAddressCityKey => :city,
|
59
87
|
KABPersonAddressStateKey => :state,
|
@@ -71,20 +99,61 @@ module AddressBook
|
|
71
99
|
KABPersonInstantMessageUsernameKey => :username
|
72
100
|
}
|
73
101
|
|
102
|
+
LabelMap = {
|
103
|
+
"mobile" => KABPersonPhoneMobileLabel ,
|
104
|
+
"iphone" => KABPersonPhoneIPhoneLabel ,
|
105
|
+
"main" => KABPersonPhoneMainLabel ,
|
106
|
+
"home_fax" => KABPersonPhoneHomeFAXLabel,
|
107
|
+
"work_fax" => KABPersonPhoneWorkFAXLabel,
|
108
|
+
"pager" => KABPersonPhonePagerLabel ,
|
109
|
+
"work" => KABWorkLabel ,
|
110
|
+
"home" => KABHomeLabel ,
|
111
|
+
"other" => KABOtherLabel ,
|
112
|
+
"home page"=> KABPersonHomePageLabel,
|
113
|
+
"anniversary"=> KABPersonAnniversaryLabel
|
114
|
+
}
|
115
|
+
|
74
116
|
def dict_to_ab_record(h)
|
75
|
-
|
117
|
+
h = PropertyMap.each_with_object({}) do |(ab_key, attr_key), ab_record|
|
76
118
|
ab_record[ab_key] = h[attr_key] if h[attr_key]
|
77
119
|
end
|
120
|
+
h.any? ? h : nil
|
78
121
|
end
|
79
122
|
|
80
|
-
def ab_record_to_dict(
|
81
|
-
case
|
82
|
-
when
|
83
|
-
{:value =>
|
84
|
-
|
85
|
-
|
123
|
+
def ab_record_to_dict(i)
|
124
|
+
case multi_value_property_type
|
125
|
+
when KABStringPropertyType
|
126
|
+
{:value => ABMultiValueCopyValueAtIndex(@ab_multi_value, i)}
|
127
|
+
when KABDateTimePropertyType
|
128
|
+
{:date => ABHack.getDateValueAtIndex(i, from: @ab_multi_value)}
|
129
|
+
when KABDictionaryPropertyType
|
130
|
+
ab_record = ABMultiValueCopyValueAtIndex(@ab_multi_value, i)
|
131
|
+
PropertyMap.each_with_object({}) do |(ab_key, attr_key), dict|
|
86
132
|
dict[attr_key] = ab_record[ab_key] if ab_record[ab_key]
|
87
133
|
end
|
134
|
+
else
|
135
|
+
raise TypeError, "Unknown MultiValue property type"
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
def <<(rec)
|
140
|
+
case multi_value_property_type
|
141
|
+
when KABMultiStringPropertyType
|
142
|
+
ABMultiValueAddValueAndLabel(ab_multi_value, rec[:value], localized_label(rec[:label]), nil)
|
143
|
+
when KABMultiDateTimePropertyType
|
144
|
+
ABMultiValueAddValueAndLabel(ab_multi_value, rec[:date], localized_label(rec[:label]), nil)
|
145
|
+
when KABMultiDictionaryPropertyType
|
146
|
+
ABMultiValueAddValueAndLabel(ab_multi_value, dict_to_ab_record(rec), localized_label(rec[:label]), nil)
|
147
|
+
else
|
148
|
+
raise TypeError, "Unknown MultiValue property type"
|
149
|
+
end
|
150
|
+
|
151
|
+
@attributes = convert_multi_value_into_dictionary
|
152
|
+
end
|
153
|
+
|
154
|
+
def first_for(label)
|
155
|
+
if rec = attributes.find {|r| r[:label] == label.to_s}
|
156
|
+
rec[:value] ? rec[:value] : rec
|
88
157
|
end
|
89
158
|
end
|
90
159
|
end
|
@@ -75,15 +75,15 @@ module AddressBook
|
|
75
75
|
def self.attribute_map
|
76
76
|
{
|
77
77
|
:first_name => KABPersonFirstNameProperty,
|
78
|
-
:middle_name
|
78
|
+
:middle_name => KABPersonMiddleNameProperty,
|
79
79
|
:last_name => KABPersonLastNameProperty,
|
80
|
-
:suffix
|
81
|
-
:nickname
|
80
|
+
:suffix => KABPersonSuffixProperty,
|
81
|
+
:nickname => KABPersonNicknameProperty,
|
82
82
|
:job_title => KABPersonJobTitleProperty,
|
83
83
|
:department => KABPersonDepartmentProperty,
|
84
84
|
:organization => KABPersonOrganizationProperty,
|
85
|
-
|
86
|
-
:note
|
85
|
+
:birthday => KABPersonBirthdayProperty,
|
86
|
+
:note => KABPersonNoteProperty
|
87
87
|
}
|
88
88
|
end
|
89
89
|
def attribute_map
|
@@ -93,6 +93,8 @@ module AddressBook
|
|
93
93
|
def method_missing(name, *args)
|
94
94
|
if attribute_name = getter?(name)
|
95
95
|
get(attribute_name)
|
96
|
+
# if getter?(name)
|
97
|
+
# get(name)
|
96
98
|
elsif attribute_name = setter?(name)
|
97
99
|
set(attribute_name, args.first)
|
98
100
|
else
|
@@ -119,8 +121,15 @@ module AddressBook
|
|
119
121
|
def getter?(method_name)
|
120
122
|
if self.class.is_attribute? method_name
|
121
123
|
method_name
|
124
|
+
# true
|
122
125
|
else
|
123
126
|
nil
|
127
|
+
# attribute = method_name.split('_').last
|
128
|
+
# if ['email', 'phone'].include?(attribute)
|
129
|
+
# true
|
130
|
+
# else
|
131
|
+
# false
|
132
|
+
# end
|
124
133
|
end
|
125
134
|
end
|
126
135
|
def setter?(method_name)
|
@@ -157,6 +166,8 @@ module AddressBook
|
|
157
166
|
end
|
158
167
|
|
159
168
|
def get(attribute_name)
|
169
|
+
# label, attribute = attribute_name.split('_')
|
170
|
+
# self.send("#{attribute}s").first_for(label)
|
160
171
|
attributes[attribute_name.to_sym] ||= get_field(attribute_map[attribute_name])
|
161
172
|
end
|
162
173
|
|
@@ -187,6 +198,10 @@ module AddressBook
|
|
187
198
|
find_by(attribute_name, criteria) || new_by(attribute_name, criteria)
|
188
199
|
end
|
189
200
|
|
201
|
+
def photo_image
|
202
|
+
UIImage.alloc.initWithData(photo)
|
203
|
+
end
|
204
|
+
|
190
205
|
def photo
|
191
206
|
ABPersonCopyImageData(ab_person)
|
192
207
|
end
|
@@ -196,7 +211,9 @@ module AddressBook
|
|
196
211
|
end
|
197
212
|
|
198
213
|
def get_multi_valued(field)
|
199
|
-
|
214
|
+
if mv = ABRecordCopyValue(ab_person, field)
|
215
|
+
MultiValued.new(:ab_multi_value => mv)
|
216
|
+
end
|
200
217
|
end
|
201
218
|
|
202
219
|
def phones
|
@@ -235,6 +252,10 @@ module AddressBook
|
|
235
252
|
get_multi_valued(KABPersonRelatedNamesProperty)
|
236
253
|
end
|
237
254
|
|
255
|
+
def dates
|
256
|
+
get_multi_valued(KABPersonDateProperty)
|
257
|
+
end
|
258
|
+
|
238
259
|
def email; email_values.first; end
|
239
260
|
def phone; phone_values.first; end
|
240
261
|
def url; urls.attributes.first[:value]; end
|
@@ -276,18 +297,56 @@ module AddressBook
|
|
276
297
|
get_field(KABPersonKindProperty) == KABPersonKindOrganization
|
277
298
|
end
|
278
299
|
|
279
|
-
|
300
|
+
def modification_date
|
301
|
+
# workaround for RubyMotion bug: blows up when fetching NSDate properties
|
302
|
+
# see http://hipbyte.myjetbrains.com/youtrack/issue/RM-81
|
303
|
+
# still broken in RubyMotion 2.0
|
304
|
+
ABHack.getDateProperty(KABPersonModificationDateProperty, from: ab_person)
|
305
|
+
# when RubyMotion bug is fixed, this should just be
|
306
|
+
# get_field(KABPersonModificationDateProperty)
|
307
|
+
end
|
308
|
+
|
280
309
|
def creation_date
|
281
|
-
|
310
|
+
ABHack.getDateProperty(KABPersonCreationDateProperty, from: ab_person)
|
282
311
|
end
|
283
312
|
|
284
|
-
|
285
|
-
|
313
|
+
# replace *all* properties of an existing Person with new values
|
314
|
+
def replace(new_attributes)
|
315
|
+
@attributes = new_attributes
|
316
|
+
load_ab_person
|
317
|
+
end
|
318
|
+
|
319
|
+
def source
|
320
|
+
s = ABPersonCopySource(ab_person)
|
321
|
+
Source.new(s)
|
322
|
+
# fetching KABSourceNameProperty always seems to return NULL
|
323
|
+
# ABRecordCopyValue(s, KABSourceTypeProperty)
|
324
|
+
end
|
325
|
+
|
326
|
+
def linked_people
|
327
|
+
recs = ABPersonCopyArrayOfAllLinkedPeople(ab_person).mutableCopy
|
328
|
+
recs.delete(ab_person) # LinkedPeople always includes self
|
329
|
+
recs.map do |linked_rec|
|
330
|
+
Person.new(nil, linked_rec, :address_book => address_book)
|
331
|
+
end
|
332
|
+
end
|
333
|
+
|
334
|
+
def to_vcard
|
335
|
+
self.class.vcard_for(self)
|
336
|
+
end
|
337
|
+
|
338
|
+
def self.vcard_for(people)
|
339
|
+
if people.respond_to? :map
|
340
|
+
ab_persons = people.map(&:ab_person)
|
341
|
+
else
|
342
|
+
ab_persons = [people.ab_person]
|
343
|
+
end
|
344
|
+
ABPersonCreateVCardRepresentationWithPeople(ab_persons)
|
286
345
|
end
|
287
346
|
|
288
347
|
private
|
289
348
|
|
290
|
-
def single_value_property_map
|
349
|
+
def self.single_value_property_map
|
291
350
|
{
|
292
351
|
KABPersonFirstNameProperty => :first_name,
|
293
352
|
KABPersonLastNameProperty => :last_name,
|
@@ -297,12 +356,12 @@ module AddressBook
|
|
297
356
|
KABPersonJobTitleProperty => :job_title,
|
298
357
|
KABPersonDepartmentProperty => :department,
|
299
358
|
KABPersonOrganizationProperty => :organization,
|
300
|
-
|
359
|
+
KABPersonBirthdayProperty => :birthday,
|
301
360
|
KABPersonNoteProperty => :note
|
302
361
|
}
|
303
362
|
end
|
304
363
|
|
305
|
-
def multi_value_property_map
|
364
|
+
def self.multi_value_property_map
|
306
365
|
{
|
307
366
|
KABPersonPhoneProperty => :phones,
|
308
367
|
KABPersonEmailProperty => :emails,
|
@@ -310,7 +369,8 @@ module AddressBook
|
|
310
369
|
KABPersonURLProperty => :urls,
|
311
370
|
KABPersonSocialProfileProperty => :social_profiles,
|
312
371
|
KABPersonInstantMessageProperty => :im_profiles,
|
313
|
-
KABPersonRelatedNamesProperty => :related_names
|
372
|
+
KABPersonRelatedNamesProperty => :related_names,
|
373
|
+
KABPersonDateProperty => :dates
|
314
374
|
}
|
315
375
|
end
|
316
376
|
|
@@ -318,9 +378,11 @@ module AddressBook
|
|
318
378
|
def load_ab_person
|
319
379
|
@attributes ||= {}
|
320
380
|
|
321
|
-
single_value_property_map.each do |ab_property, attr_key|
|
381
|
+
Person.single_value_property_map.each do |ab_property, attr_key|
|
322
382
|
if attributes[attr_key]
|
323
383
|
set_field(ab_property, attributes[attr_key])
|
384
|
+
else
|
385
|
+
remove_field(ab_property)
|
324
386
|
end
|
325
387
|
end
|
326
388
|
|
@@ -330,16 +392,23 @@ module AddressBook
|
|
330
392
|
set_field(KABPersonKindProperty, KABPersonKindPerson)
|
331
393
|
end
|
332
394
|
|
333
|
-
multi_value_property_map.each do |ab_property, attr_key|
|
395
|
+
Person.multi_value_property_map.each do |ab_property, attr_key|
|
334
396
|
if attributes[attr_key]
|
335
397
|
set_multi_valued(ab_property, attributes[attr_key])
|
398
|
+
else
|
399
|
+
remove_field(ab_property)
|
336
400
|
end
|
337
401
|
end
|
402
|
+
|
403
|
+
ab_person
|
338
404
|
end
|
339
405
|
|
406
|
+
# populate attributes from existing ABPerson
|
340
407
|
def import_ab_person
|
341
408
|
@attributes = {}
|
342
|
-
|
409
|
+
@modification_date = nil
|
410
|
+
|
411
|
+
Person.single_value_property_map.each do |ab_property, attr_key|
|
343
412
|
if value = get_field(ab_property)
|
344
413
|
@attributes[attr_key] = value
|
345
414
|
end
|
@@ -349,9 +418,11 @@ module AddressBook
|
|
349
418
|
@attributes[:is_org] = true
|
350
419
|
end
|
351
420
|
|
352
|
-
multi_value_property_map.each do |ab_property, attr_key|
|
353
|
-
if
|
354
|
-
|
421
|
+
Person.multi_value_property_map.each do |ab_property, attr_key|
|
422
|
+
if value = get_multi_valued(ab_property)
|
423
|
+
if value.attributes.any?
|
424
|
+
@attributes[attr_key] = value.attributes
|
425
|
+
end
|
355
426
|
end
|
356
427
|
end
|
357
428
|
|
@@ -364,10 +435,20 @@ module AddressBook
|
|
364
435
|
end
|
365
436
|
end
|
366
437
|
def get_field(field)
|
367
|
-
|
438
|
+
if field == KABPersonBirthdayProperty
|
439
|
+
# special case: RubyMotion blows up on NSDate properties
|
440
|
+
# see http://hipbyte.myjetbrains.com/youtrack/issue/RM-81
|
441
|
+
ABHack.getDateProperty(field, from: ab_person)
|
442
|
+
else
|
443
|
+
ABRecordCopyValue(ab_person, field)
|
444
|
+
end
|
445
|
+
end
|
446
|
+
def remove_field(field)
|
447
|
+
ABRecordRemoveValue(ab_person, field, nil)
|
368
448
|
end
|
369
449
|
|
370
450
|
def set_multi_valued(field, values)
|
451
|
+
values = values.map { |value| ( (value.kind_of?String) ? {:value => value} : value)}
|
371
452
|
if values && values.any?
|
372
453
|
multi_field = MultiValued.new(:attributes => values)
|
373
454
|
ABRecordSetValue(ab_person, field, multi_field.ab_multi_value, nil)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module AddressBook
|
2
|
+
class Source
|
3
|
+
attr_reader :ab_source
|
4
|
+
|
5
|
+
def initialize(ab_source)
|
6
|
+
@ab_source = ab_source
|
7
|
+
end
|
8
|
+
|
9
|
+
def type
|
10
|
+
ABRecordCopyValue(ab_source, KABSourceTypeProperty)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def local?
|
15
|
+
type == KABSourceTypeLocal
|
16
|
+
end
|
17
|
+
end
|
data/motion-addressbook.gemspec
CHANGED
@@ -2,8 +2,11 @@
|
|
2
2
|
require File.expand_path('../lib/motion-addressbook/version', __FILE__)
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
|
-
gem.
|
6
|
-
gem.
|
5
|
+
gem.name = "motion-addressbook"
|
6
|
+
gem.version = Motion::Addressbook::VERSION
|
7
|
+
|
8
|
+
gem.authors = ["Alex Rothenberg", "Jason May"]
|
9
|
+
gem.email = ["alex@alexrothenberg.com", "jmay@pobox.com"]
|
7
10
|
gem.description = %q{A RubyMotion wrapper around the iOS Address Book framework}
|
8
11
|
gem.summary = %q{A RubyMotion wrapper around the iOS Address Book framework}
|
9
12
|
gem.homepage = ""
|
@@ -11,11 +14,10 @@ Gem::Specification.new do |gem|
|
|
11
14
|
gem.files = `git ls-files`.split($\)
|
12
15
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
13
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
-
gem.name = "motion-addressbook"
|
15
17
|
gem.require_paths = ["lib"]
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
gem.add_development_dependency 'rake'
|
18
|
+
|
19
|
+
gem.add_dependency 'bubble-wrap', '~> 1.3'
|
20
|
+
|
21
|
+
gem.add_development_dependency 'rake'
|
20
22
|
gem.add_development_dependency 'rspec'
|
21
23
|
end
|
@@ -23,6 +23,11 @@ describe AddressBook::Group do
|
|
23
23
|
it "should not be new" do
|
24
24
|
@group.should.not.be.new
|
25
25
|
end
|
26
|
+
|
27
|
+
it "should not add unsaved person records" do
|
28
|
+
p = @ab.new_person({:first_name => 'Alice', :last_name => 'Artichoke'})
|
29
|
+
lambda {@group << p}.should.raise ArgumentError
|
30
|
+
end
|
26
31
|
end
|
27
32
|
|
28
33
|
describe 'a group with members' do
|
@@ -1,4 +1,18 @@
|
|
1
1
|
describe AddressBook::MultiValued do
|
2
|
+
describe 'a new multi-value' do
|
3
|
+
it 'should convert to localized labels' do
|
4
|
+
AddressBook::MultiValued::LabelMap.size.should.equal 11
|
5
|
+
AddressBook::MultiValued::LabelMap.each do |label, localized|
|
6
|
+
mv = AddressBook::MultiValued.new(:attributes => [{:label => label, :value => 'test'}])
|
7
|
+
ABMultiValueCopyLabelAtIndex(mv.ab_multi_value, 0).should.equal localized
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should not allow empty input" do
|
12
|
+
->{AddressBook::MultiValued.new(:attributes => [])}.should.raise ArgumentError
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
2
16
|
describe 'a string multi-value' do
|
3
17
|
before do
|
4
18
|
@attributes = [
|
@@ -25,6 +39,16 @@ describe AddressBook::MultiValued do
|
|
25
39
|
mv2 = AddressBook::MultiValued.new(:ab_multi_value => abmv)
|
26
40
|
mv2.attributes.should.equal @attributes
|
27
41
|
end
|
42
|
+
|
43
|
+
describe 'after appending' do
|
44
|
+
before do
|
45
|
+
@mv << {:label => 'work', :value => 'another string'}
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should have new values" do
|
49
|
+
@mv.count.should.equal 4
|
50
|
+
end
|
51
|
+
end
|
28
52
|
end
|
29
53
|
|
30
54
|
describe 'a dictionary multi-value' do
|
@@ -57,10 +81,91 @@ describe AddressBook::MultiValued do
|
|
57
81
|
it 'should have correct internal representation' do
|
58
82
|
internal = @mv.ab_multi_value
|
59
83
|
ABMultiValueGetCount(internal).should.equal 2
|
60
|
-
ABMultiValueCopyLabelAtIndex(internal, 0).should.equal
|
61
|
-
ABMultiValueCopyLabelAtIndex(internal, 1).should.equal
|
84
|
+
ABMultiValueCopyLabelAtIndex(internal, 0).should.equal KABHomeLabel
|
85
|
+
ABMultiValueCopyLabelAtIndex(internal, 1).should.equal KABWorkLabel
|
62
86
|
ABMultiValueCopyValueAtIndex(internal, 0).keys.count.should.equal 3
|
63
87
|
ABMultiValueCopyValueAtIndex(internal, 1).keys.count.should.equal 3
|
64
88
|
end
|
89
|
+
|
90
|
+
describe 'after appending' do
|
91
|
+
before do
|
92
|
+
@mv << {:label => 'summer', :city => 'Key West', :state => 'FL'}
|
93
|
+
end
|
94
|
+
|
95
|
+
it "should have new values" do
|
96
|
+
@mv.count.should.equal 3
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe 'a date multi-value' do
|
102
|
+
before do
|
103
|
+
@attributes = [
|
104
|
+
{
|
105
|
+
:label => 'birthday',
|
106
|
+
:date => NSDate.dateWithNaturalLanguageString('April 5, 1962')
|
107
|
+
}, {
|
108
|
+
:label => 'anniversary',
|
109
|
+
:date => NSDate.dateWithNaturalLanguageString('September 22, 1994')
|
110
|
+
}, {
|
111
|
+
:label => 'death',
|
112
|
+
:date => NSDate.dateWithNaturalLanguageString('December 1, 2008')
|
113
|
+
}
|
114
|
+
]
|
115
|
+
@mv = AddressBook::MultiValued.new(:attributes => @attributes)
|
116
|
+
end
|
117
|
+
|
118
|
+
it 'should be countable' do
|
119
|
+
@mv.count.should.equal 3
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should be reversible' do
|
123
|
+
abmv = @mv.ab_multi_value
|
124
|
+
mv2 = AddressBook::MultiValued.new(:ab_multi_value => abmv)
|
125
|
+
mv2.attributes.should.equal @attributes
|
126
|
+
# 3.should.equal 3
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should not explode' do
|
130
|
+
abmv = @mv.ab_multi_value
|
131
|
+
mv2 = AddressBook::MultiValued.new(:ab_multi_value => abmv)
|
132
|
+
dt = mv2.attributes[1][:date]
|
133
|
+
t = NSDate.dateWithNaturalLanguageString('September 22, 1994')
|
134
|
+
dt.should.equal t
|
135
|
+
dt.should.equal t
|
136
|
+
dt.should.equal t
|
137
|
+
end
|
138
|
+
|
139
|
+
describe 'after appending' do
|
140
|
+
before do
|
141
|
+
@mv << {:label => 'graduation', :date => NSDate.dateWithNaturalLanguageString('June 1, 1983')}
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should have new values" do
|
145
|
+
@mv.count.should.equal 4
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
describe 'a broken multi-value' do
|
151
|
+
before do
|
152
|
+
@attributes = [{:label => 'work', :value => nil}]
|
153
|
+
@mv = AddressBook::MultiValued.new(:attributes => @attributes)
|
154
|
+
end
|
155
|
+
|
156
|
+
it 'should ignore the missing entry' do
|
157
|
+
@mv.size.should.equal 0
|
158
|
+
end
|
159
|
+
end
|
160
|
+
|
161
|
+
describe 'a date multi-value' do
|
162
|
+
before do
|
163
|
+
@attributes = [{:label => 'anniversary', :date => Time.now}]
|
164
|
+
@mv = AddressBook::MultiValued.new(:attributes => @attributes)
|
165
|
+
end
|
166
|
+
|
167
|
+
it 'should round-trip input' do
|
168
|
+
@mv.attributes.should.equal @attributes
|
169
|
+
end
|
65
170
|
end
|
66
171
|
end
|
@@ -11,6 +11,9 @@ describe AddressBook::Person do
|
|
11
11
|
it 'should create but not save in the address book' do
|
12
12
|
@alex.should.be.new_record
|
13
13
|
end
|
14
|
+
it 'should have no mod date' do
|
15
|
+
@alex.modification_date.should.be.nil
|
16
|
+
end
|
14
17
|
it 'should have initial values' do
|
15
18
|
@alex.first_name.should == 'Alex'
|
16
19
|
@alex.last_name.should == 'Testy'
|
@@ -28,11 +31,15 @@ describe AddressBook::Person do
|
|
28
31
|
describe 'existing' do
|
29
32
|
before do
|
30
33
|
@email = unique_email
|
31
|
-
@
|
34
|
+
@origdata = new_alex(@email)
|
35
|
+
@alex = @ab.create_person(@origdata)
|
32
36
|
end
|
33
37
|
after do
|
34
38
|
@alex.delete!
|
35
39
|
end
|
40
|
+
it 'should have a mod date' do
|
41
|
+
@alex.modification_date.should.not.be.nil
|
42
|
+
end
|
36
43
|
describe '.find_by_uid' do
|
37
44
|
it 'should find match' do
|
38
45
|
alex = @ab.person(@alex.uid)
|
@@ -101,6 +108,24 @@ describe AddressBook::Person do
|
|
101
108
|
@person.delete!
|
102
109
|
end
|
103
110
|
end
|
111
|
+
|
112
|
+
describe ".replace" do
|
113
|
+
before do
|
114
|
+
warn "DOING REPLACE"
|
115
|
+
@newdata = {
|
116
|
+
:first_name => 'Alexander',
|
117
|
+
:last_name => "Testy",
|
118
|
+
:organization => "Acme, Inc.",
|
119
|
+
:emails => [{:label => 'work', :value => @origdata[:emails].first[:value]}]
|
120
|
+
}
|
121
|
+
@alex.replace(@newdata)
|
122
|
+
@alex.save
|
123
|
+
end
|
124
|
+
|
125
|
+
it "should have the new contents" do
|
126
|
+
@alex.attributes.should == @newdata
|
127
|
+
end
|
128
|
+
end
|
104
129
|
end
|
105
130
|
|
106
131
|
describe '.find_or_new_by_XXX - new or existing' do
|
@@ -141,6 +166,7 @@ describe AddressBook::Person do
|
|
141
166
|
:department => 'Development',
|
142
167
|
:organization => 'The Company',
|
143
168
|
:note => 'some important guy',
|
169
|
+
:birthday => NSDate.dateWithNaturalLanguageString('July 1, 1982'),
|
144
170
|
# :mobile_phone => '123 456 7890', :office_phone => '987 654 3210',
|
145
171
|
:phones => [
|
146
172
|
{:label => 'mobile', :value => '123 456 7899'},
|
@@ -157,6 +183,10 @@ describe AddressBook::Person do
|
|
157
183
|
{ :label => 'home page', :value => "http://www.mysite.com/" },
|
158
184
|
{ :label => 'work', :value => 'http://dept.bigco.com/' },
|
159
185
|
{ :label => 'school', :value => 'http://state.edu/college' }
|
186
|
+
],
|
187
|
+
:dates => [
|
188
|
+
{ :label => 'anniversary', :date => NSDate.dateWithNaturalLanguageString('October 9, 2009') },
|
189
|
+
{ :label => 'apotheosis', :date => NSDate.dateWithNaturalLanguageString('April 1, 2013') }
|
160
190
|
]
|
161
191
|
}
|
162
192
|
end
|
@@ -169,6 +199,7 @@ describe AddressBook::Person do
|
|
169
199
|
it 'should not be existing' do
|
170
200
|
@ab_person.should.be.new_record
|
171
201
|
@ab_person.should.not.be.exists
|
202
|
+
@ab_person.modification_date.should.be.nil
|
172
203
|
end
|
173
204
|
|
174
205
|
it 'should be able to get each of the single value fields' do
|
@@ -213,11 +244,26 @@ describe AddressBook::Person do
|
|
213
244
|
@ab_person.organization.should.equal 'new organization'
|
214
245
|
end
|
215
246
|
|
247
|
+
def empty_image(width, height)
|
248
|
+
UIGraphicsBeginImageContext(CGSizeMake(width, height) )
|
249
|
+
CGContextSetFillColorWithColor(UIGraphicsGetCurrentContext(), '#ffffff'.to_color)
|
250
|
+
image = UIGraphicsGetImageFromCurrentImageContext()
|
251
|
+
UIGraphicsEndImageContext()
|
252
|
+
image
|
253
|
+
end
|
254
|
+
|
216
255
|
it 'should be able to set the photo' do
|
217
|
-
|
218
|
-
data = UIImagePNGRepresentation(UIImage.imageWithCIImage image)
|
256
|
+
data = UIImagePNGRepresentation empty_image(1,1)
|
219
257
|
@ab_person.photo = data
|
220
|
-
|
258
|
+
@ab_person.photo.should.not == nil
|
259
|
+
@ab_person.photo.should.equal data
|
260
|
+
end
|
261
|
+
|
262
|
+
it 'should be able to get the photo as an image' do
|
263
|
+
data = UIImagePNGRepresentation empty_image(1,1)
|
264
|
+
@ab_person.photo = data
|
265
|
+
@ab_person.photo_image.size
|
266
|
+
@ab_person.photo_image.size.should.equal CGSizeMake(1,1)
|
221
267
|
end
|
222
268
|
end
|
223
269
|
|
@@ -243,6 +289,7 @@ describe AddressBook::Person do
|
|
243
289
|
|
244
290
|
describe 'once saved' do
|
245
291
|
before do
|
292
|
+
@before = Time.now
|
246
293
|
@before_count = AddressBook.count
|
247
294
|
@ab_person.save
|
248
295
|
end
|
@@ -255,6 +302,11 @@ describe AddressBook::Person do
|
|
255
302
|
@ab_person.should.be.exists
|
256
303
|
end
|
257
304
|
|
305
|
+
# it 'should populate timestamps' do
|
306
|
+
# @ab_person.modification_date.should.not.be.nil
|
307
|
+
# should.satisfy {@ab_person.modification_date > @before}
|
308
|
+
# end
|
309
|
+
|
258
310
|
it "should increment the count" do
|
259
311
|
AddressBook.count.should.equal @before_count+1
|
260
312
|
end
|
@@ -264,7 +316,7 @@ describe AddressBook::Person do
|
|
264
316
|
end
|
265
317
|
|
266
318
|
it 'should have scalar properties' do
|
267
|
-
[:first_name, :middle_name, :last_name, :job_title, :department, :organization, :note].each do |attr|
|
319
|
+
[:first_name, :middle_name, :last_name, :job_title, :department, :organization, :note, :birthday].each do |attr|
|
268
320
|
@ab_person.send(attr).should.equal @attributes[attr]
|
269
321
|
end
|
270
322
|
end
|
@@ -371,7 +423,8 @@ describe AddressBook::Person do
|
|
371
423
|
:last_name => 'Whorfin',
|
372
424
|
:organization => 'Acme Inc.',
|
373
425
|
:is_org => true,
|
374
|
-
:note => 'big important company'
|
426
|
+
:note => 'big important company',
|
427
|
+
:birthday => NSDate.dateWithNaturalLanguageString('August 17, 1947')
|
375
428
|
)
|
376
429
|
end
|
377
430
|
|
@@ -391,6 +444,7 @@ describe AddressBook::Person do
|
|
391
444
|
@person.getter?('job_title' ).should.be truthy
|
392
445
|
@person.getter?('department' ).should.be truthy
|
393
446
|
@person.getter?('organization').should.be truthy
|
447
|
+
@person.getter?('birthday').should.be truthy
|
394
448
|
end
|
395
449
|
it 'should know what is not a getter' do
|
396
450
|
@person.getter?('nonesense' ).should.be falsey
|
@@ -448,4 +502,69 @@ describe AddressBook::Person do
|
|
448
502
|
end
|
449
503
|
end
|
450
504
|
end
|
505
|
+
|
506
|
+
describe "multiple emails/phone #'s handling" do
|
507
|
+
it "should accept multiple emails/phone #'s as array of strings for new records" do
|
508
|
+
person = @ab.new_person(
|
509
|
+
:first_name => 'Ashish',
|
510
|
+
:last_name => 'Upadhyay',
|
511
|
+
:email => ['a@mail.com','a@mail.com','a@mail.com'],
|
512
|
+
:phones => ['1212999222','1212999333','1212999444'],
|
513
|
+
)
|
514
|
+
person.should.be.new_record
|
515
|
+
end
|
516
|
+
it "should accept multiple emails/phone #'s as array of hashes for new records" do
|
517
|
+
person = @ab.new_person(
|
518
|
+
:first_name => 'Ashish',
|
519
|
+
:last_name => 'Upadhyay',
|
520
|
+
:email => [{ :value => 'a@mail.com' } , { :value => 'a@mail.com' } , { :value => 'a@mail.com' } ] ,
|
521
|
+
:phones => [{ :value => '1212999222' } , { :value => '1212999333' } , { :value => '1212999444' } ] ,
|
522
|
+
)
|
523
|
+
person.should.be.new_record
|
524
|
+
end
|
525
|
+
it "should accept multiple emails/phone #'s as array of combination of strings or hashes for new records" do
|
526
|
+
person = @ab.new_person(
|
527
|
+
:first_name => 'Ashish',
|
528
|
+
:last_name => 'Upadhyay',
|
529
|
+
:email => [ { :value => 'a@mail.com' } , 'a@mail.com' , { :value => 'a@mail.com', :label => 'Office'}] ,
|
530
|
+
:phones => [ '1212999222' , { :value => '1212999333', :label => 'Personal' } , { :value => '1212999444' } ] ,
|
531
|
+
)
|
532
|
+
person.should.be.new_record
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
describe 'vcard' do
|
537
|
+
before do
|
538
|
+
@alex = @ab.create_person(new_alex(unique_email))
|
539
|
+
@jason = @ab.create_person(new_alex('jason@example.com'))
|
540
|
+
end
|
541
|
+
after do
|
542
|
+
@jason.delete!
|
543
|
+
@alex.delete!
|
544
|
+
end
|
545
|
+
|
546
|
+
describe '.vcard_for' do
|
547
|
+
it 'creates a vcard for a single person' do
|
548
|
+
alex_vcard = AddressBook::Person.vcard_for(@alex).to_s
|
549
|
+
alex_vcard.should.include? 'BEGIN:VCARD'
|
550
|
+
alex_vcard.should.include? "EMAIL;type=INTERNET;type=HOME;type=pref:#{@alex.email}"
|
551
|
+
alex_vcard.should.include? 'END:VCARD'
|
552
|
+
end
|
553
|
+
it 'creates a vcard for an array of people' do
|
554
|
+
alex_and_jason_vcard = AddressBook::Person.vcard_for([@alex, @jason]).to_s
|
555
|
+
alex_and_jason_vcard.should.include? 'BEGIN:VCARD'
|
556
|
+
alex_and_jason_vcard.should.include? "EMAIL;type=INTERNET;type=HOME;type=pref:#{@alex.email}"
|
557
|
+
alex_and_jason_vcard.should.include? "EMAIL;type=INTERNET;type=HOME;type=pref:#{@jason.email}"
|
558
|
+
alex_and_jason_vcard.should.include? 'END:VCARD'
|
559
|
+
end
|
560
|
+
end
|
561
|
+
describe '#to_vcard' do
|
562
|
+
it 'knows how to create vcard for itself' do
|
563
|
+
alex_vcard = @alex.to_vcard.to_s
|
564
|
+
alex_vcard.should.include? 'BEGIN:VCARD'
|
565
|
+
alex_vcard.should.include? "EMAIL;type=INTERNET;type=HOME;type=pref:#{@alex.email}"
|
566
|
+
alex_vcard.should.include? 'END:VCARD'
|
567
|
+
end
|
568
|
+
end
|
569
|
+
end
|
451
570
|
end
|
metadata
CHANGED
@@ -1,32 +1,33 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-addressbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Alex Rothenberg
|
9
|
+
- Jason May
|
9
10
|
autorequire:
|
10
11
|
bindir: bin
|
11
12
|
cert_chain: []
|
12
|
-
date: 2013-
|
13
|
+
date: 2013-05-17 00:00:00.000000000 Z
|
13
14
|
dependencies:
|
14
15
|
- !ruby/object:Gem::Dependency
|
15
16
|
name: bubble-wrap
|
16
17
|
requirement: !ruby/object:Gem::Requirement
|
17
18
|
none: false
|
18
19
|
requirements:
|
19
|
-
- -
|
20
|
+
- - ~>
|
20
21
|
- !ruby/object:Gem::Version
|
21
|
-
version: '
|
22
|
+
version: '1.3'
|
22
23
|
type: :runtime
|
23
24
|
prerelease: false
|
24
25
|
version_requirements: !ruby/object:Gem::Requirement
|
25
26
|
none: false
|
26
27
|
requirements:
|
27
|
-
- -
|
28
|
+
- - ~>
|
28
29
|
- !ruby/object:Gem::Version
|
29
|
-
version: '
|
30
|
+
version: '1.3'
|
30
31
|
- !ruby/object:Gem::Dependency
|
31
32
|
name: rake
|
32
33
|
requirement: !ruby/object:Gem::Requirement
|
@@ -62,6 +63,7 @@ dependencies:
|
|
62
63
|
description: A RubyMotion wrapper around the iOS Address Book framework
|
63
64
|
email:
|
64
65
|
- alex@alexrothenberg.com
|
66
|
+
- jmay@pobox.com
|
65
67
|
executables: []
|
66
68
|
extensions: []
|
67
69
|
extra_rdoc_files: []
|
@@ -72,6 +74,8 @@ files:
|
|
72
74
|
- LICENSE
|
73
75
|
- README.md
|
74
76
|
- Rakefile
|
77
|
+
- abhack/abhack.h
|
78
|
+
- abhack/abhack.m
|
75
79
|
- lib/motion-addressbook.rb
|
76
80
|
- lib/motion-addressbook/version.rb
|
77
81
|
- motion-addressbook.gemspec
|
@@ -82,6 +86,7 @@ files:
|
|
82
86
|
- motion/address_book/multi_valued.rb
|
83
87
|
- motion/address_book/person.rb
|
84
88
|
- motion/address_book/picker.rb
|
89
|
+
- motion/address_book/source.rb
|
85
90
|
- spec/address_book/group_spec.rb
|
86
91
|
- spec/address_book/multi_value_spec.rb
|
87
92
|
- spec/address_book/multi_valued_spec.rb
|
@@ -104,7 +109,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
104
109
|
version: '0'
|
105
110
|
segments:
|
106
111
|
- 0
|
107
|
-
hash:
|
112
|
+
hash: -259703607999042735
|
108
113
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
109
114
|
none: false
|
110
115
|
requirements:
|
@@ -113,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
118
|
version: '0'
|
114
119
|
segments:
|
115
120
|
- 0
|
116
|
-
hash:
|
121
|
+
hash: -259703607999042735
|
117
122
|
requirements: []
|
118
123
|
rubyforge_project:
|
119
124
|
rubygems_version: 1.8.24
|