motion-addressbook 1.2.0 → 1.4.0
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.
- 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
|