motion-addressbook 1.5.0 → 1.6.2
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -3
- data/README.md +67 -36
- data/lib/motion-addressbook/version.rb +1 -1
- data/lib/motion-addressbook.rb +13 -5
- data/motion/address_book/ios/addr_book.rb +112 -20
- data/motion/address_book/ios/group.rb +32 -17
- data/motion/address_book/ios/multi_valued.rb +17 -2
- data/motion/address_book/ios/person.rb +21 -47
- data/motion/address_book/ios/picker.rb +7 -6
- data/motion/address_book/ios/source.rb +3 -3
- data/motion/address_book/osx/addr_book.rb +9 -5
- data/motion/address_book/osx/group.rb +14 -6
- data/motion/address_book/osx/multi_valued.rb +15 -0
- data/motion/address_book/osx/person.rb +12 -9
- data/motion/address_book.rb +10 -5
- data/spec/ios/address_book/group_spec.rb +46 -6
- data/spec/ios/address_book/person_spec.rb +49 -1
- data/spec/ios/helpers/spec_helper.rb +32 -0
- data/spec/osx/address_book/person_spec.rb +9 -9
- metadata +13 -27
- data/abhack/abhack.h +0 -8
- data/abhack/abhack.m +0 -15
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c5f98fa75e972433a15e036925c3411517e2c6d6
|
4
|
+
data.tar.gz: 7585741f2762ec9275c74b2a40e70075dee8a2fd
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 6d3f794f0adefee07007dd44591f274a34b1fae7b06c341ba73b5dea7d2136962711be09818ff27d8adef86525d900e95a6c599b40ce16c9665fced6c651ec40
|
7
|
+
data.tar.gz: 192065725a196bd2bb8e05bddb436b458c479bbea4933a9870a84c40e37f96e34adc5d2a41a034878fe8b56a92c55283d338170cbb28b1faf15aff42683635c1
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -1,6 +1,10 @@
|
|
1
|
-
# Addressbook for RubyMotion
|
1
|
+
# Addressbook for RubyMotion
|
2
2
|
|
3
|
-
|
3
|
+
[](http://travis-ci.org/alexrothenberg/motion-addressbook)
|
4
|
+
[](https://codeclimate.com/github/alexrothenberg/motion-addressbook)
|
5
|
+
[](http://badge.fury.io/rb/motion-addressbook)
|
6
|
+
|
7
|
+
A RubyMotion wrapper around the iOS and OSX Address Book frameworks for RubyMotion apps.
|
4
8
|
|
5
9
|
Apple's Address Book Programming Guide for [iOS](http://developer.apple.com/library/ios/#DOCUMENTATION/ContactData/Conceptual/AddressBookProgrammingGuideforiPhone/Introduction.html)
|
6
10
|
or for [OSX](https://developer.apple.com/library/mac/#documentation/userexperience/Conceptual/AddressBook/AddressBook.html#//apple_ref/doc/uid/10000117i)
|
@@ -33,26 +37,30 @@ Or install it yourself (remember to add the bubble-wrap dependency) as:
|
|
33
37
|
|
34
38
|
### Requesting access
|
35
39
|
|
36
|
-
iOS 6 requires
|
40
|
+
iOS 6/7 requires that the user give permission before it allows an app to access the AddressBook.
|
37
41
|
|
38
42
|
1 - Let the gem take care of it for you
|
39
43
|
|
40
44
|
```ruby
|
41
|
-
|
42
|
-
#
|
45
|
+
ab = AddressBook::AddrBook.new
|
46
|
+
# ...do something else...
|
47
|
+
people = ab.people
|
43
48
|
```
|
44
49
|
|
50
|
+
The `people` method will raise an exception if called while
|
51
|
+
authorization has not been granted.
|
52
|
+
|
45
53
|
2 - Manually decide when to ask the user for authorization
|
46
54
|
|
47
55
|
```ruby
|
48
56
|
# asking whether we are already authorized
|
49
57
|
if AddressBook.authorized?
|
50
|
-
puts "This app is authorized
|
58
|
+
puts "This app is authorized!"
|
51
59
|
else
|
52
|
-
puts "This app is not authorized
|
60
|
+
puts "This app is not authorized!"
|
53
61
|
end
|
54
62
|
|
55
|
-
# ask the user to authorize us
|
63
|
+
# ask the user to authorize us (blocking)
|
56
64
|
if AddressBook.request_authorization
|
57
65
|
# do something now that the user has said "yes"
|
58
66
|
else
|
@@ -75,6 +83,9 @@ end
|
|
75
83
|
# do something here before the user has decided
|
76
84
|
```
|
77
85
|
|
86
|
+
The iOS6 simulator does not demand AddressBook authorization. The iOS7
|
87
|
+
simulator does.
|
88
|
+
|
78
89
|
### Showing the ABPeoplePicker
|
79
90
|
|
80
91
|
```ruby
|
@@ -87,67 +98,76 @@ AddressBook.pick { |person|
|
|
87
98
|
}
|
88
99
|
```
|
89
100
|
|
90
|
-
|
101
|
+
You can also specify the presenting controller:
|
91
102
|
|
92
|
-
|
103
|
+
```ruby
|
104
|
+
AddressBook.pick presenter: self do |person|
|
105
|
+
...
|
106
|
+
end
|
107
|
+
```
|
108
|
+
|
109
|
+
### Working with Person objects
|
93
110
|
|
94
|
-
|
111
|
+
Get a list of existing people from the Address Book. On IOS, results
|
112
|
+
are sorted using the sort order (First/Last or Last/First) chosen by
|
113
|
+
the user in iOS Settings.
|
95
114
|
|
96
115
|
```ruby
|
97
|
-
AddressBook::
|
98
|
-
|
116
|
+
ab = AddressBook::AddrBook.new
|
117
|
+
ab.people
|
118
|
+
=> [#<AddressBook::Person:3: {:first_name=>"John", :last_name=>"Appleseed", ...}>, ...]
|
99
119
|
```
|
100
120
|
|
101
|
-
|
121
|
+
Create a new Person and save to the Address Book.
|
122
|
+
|
123
|
+
Note that Person records can take multiple values for email addresses, phone
|
124
|
+
numbers, postal address, social profiles, and instant messaging
|
125
|
+
profiles.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
ab.create_person(:first_name => 'Alex', :last_name => 'Rothenberg', :emails => [{ :value => 'alex@example.com', :label => 'Home'}], :phones => [{ :value => '9920149993', :label => 'Mobile'}])
|
129
|
+
=> #<AddressBook::Person:7: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>
|
130
|
+
```
|
102
131
|
|
103
|
-
|
132
|
+
Construct a new blank Person but do not store it immediately in the Address Book.
|
104
133
|
|
105
134
|
```ruby
|
106
|
-
|
107
|
-
|
108
|
-
|
135
|
+
ab.new_person(:first_name => "Bob")
|
136
|
+
=> #<AddressBook::Person:-1: {:first_name=>"Bob"}>
|
137
|
+
ab.last_name = 'Brown'
|
138
|
+
ab.save
|
139
|
+
=> #<AddressBook::Person:9: {:first_name=>"Bob", :last_name=>"Brown"}>
|
109
140
|
```
|
110
141
|
|
111
142
|
Get a list of all people matching one attribute with `.find_all_by_XXX`
|
112
143
|
|
113
144
|
```ruby
|
114
145
|
AddressBook::Person.find_all_by_email('alex@example.com')
|
115
|
-
|
146
|
+
=> [#<AddressBook::Person:14: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
|
116
147
|
```
|
117
148
|
|
118
|
-
Get the first person matching one attribute with
|
149
|
+
Get the first person matching one attribute with `.find_by_XXX`
|
119
150
|
|
120
151
|
```ruby
|
121
152
|
AddressBook::Person.find_by_email('alex@example.com')
|
122
|
-
|
153
|
+
=> #<AddressBook::Person:14: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
|
123
154
|
```
|
124
155
|
|
125
156
|
Get a list of all people matching several attributes with `.where`
|
126
157
|
|
127
158
|
```ruby
|
128
159
|
AddressBook::Person.where(:email => 'alex@example.com', :first_name => 'Alex')
|
129
|
-
|
160
|
+
=> [#<AddressBook::Person:14: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
|
130
161
|
```
|
131
162
|
|
132
|
-
|
163
|
+
Look for an existing person or get a new one if none is found `find_or_new_by_XXX`
|
133
164
|
|
134
165
|
```ruby
|
135
166
|
AddressBook::Person.find_or_new_by_email('alex@example.com')
|
136
|
-
|
167
|
+
=> #<AddressBook::Person:17: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
|
137
168
|
```
|
138
169
|
|
139
|
-
###
|
140
|
-
|
141
|
-
```ruby
|
142
|
-
AddressBook::Person.create(:first_name => 'Alex', :last_name => 'Rothenberg', :email => [{ :value => 'alex@example.com', :label => 'Home'}], , :phones => [{ :value => '9920149993', :label => 'Mobile'}])
|
143
|
-
# => #<AddressBook::Person:0xe4e3a80 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0xe4bbef0>>
|
144
|
-
|
145
|
-
# Multiple emails/phones ex.
|
146
|
-
|
147
|
-
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'])
|
148
|
-
=> #<AddressBook::Person:0x9ce23b0 @address_book=#<__NSCFType:0x9ce2660> @ab_person=#<__NSCFType:0x9ce2450> @attributes=nil>
|
149
|
-
```
|
150
|
-
### Update existing contact
|
170
|
+
### Update existing Person
|
151
171
|
|
152
172
|
```ruby
|
153
173
|
alex = AddressBook::Person.find_by_email('alex@example.com')
|
@@ -155,6 +175,17 @@ alex.job_title = 'RubyMotion Developer'
|
|
155
175
|
alex.save
|
156
176
|
```
|
157
177
|
|
178
|
+
### Contact Groups
|
179
|
+
|
180
|
+
```ruby
|
181
|
+
ab.groups
|
182
|
+
=> [#<AddressBook::Group:1:Friends: 1 members>, #<AddressBook::Group:2:Work: 0 members>]
|
183
|
+
|
184
|
+
g = ab.groups.first
|
185
|
+
g.members
|
186
|
+
=> [#<AddressBook::Person:2: {:first_name=>"Daniel", :last_name=>"Higgins", ...}>]
|
187
|
+
```
|
188
|
+
|
158
189
|
## Contributing
|
159
190
|
|
160
191
|
1. Fork it
|
data/lib/motion-addressbook.rb
CHANGED
@@ -1,5 +1,18 @@
|
|
1
1
|
require "motion-addressbook/version"
|
2
2
|
|
3
|
+
# RubyMotion bug RM-81 was fixed for 2.8; motion-addressbook
|
4
|
+
if Gem::Version.new(Motion::Version) < Gem::Version.new("2.8")
|
5
|
+
raise <<EOT
|
6
|
+
motion-addressbook requires at least RubyMotion version 2.8.
|
7
|
+
|
8
|
+
If you cannot upgrade RubyMotion please use an older version of this gem.
|
9
|
+
Add the following to your Gemfile:
|
10
|
+
|
11
|
+
gem 'motion-addressbook', '<= 1.5.0'
|
12
|
+
|
13
|
+
EOT
|
14
|
+
end
|
15
|
+
|
3
16
|
BubbleWrap.require 'motion/address_book.rb' do
|
4
17
|
file('motion/address_book.rb').uses_framework('AddressBook')
|
5
18
|
end
|
@@ -12,11 +25,6 @@ BubbleWrap.require_ios do
|
|
12
25
|
BW.require 'motion/address_book/ios/multi_valued.rb'
|
13
26
|
BW.require 'motion/address_book/ios/source.rb'
|
14
27
|
|
15
|
-
# This is an iOS-specific RubyMotion bug workaround.
|
16
|
-
Motion::Project::App.setup do |app|
|
17
|
-
app.vendor_project(File.expand_path(File.join(File.dirname(__FILE__), '../abhack')), :static)
|
18
|
-
end
|
19
|
-
|
20
28
|
BW.require 'motion/address_book/ios/picker.rb' do
|
21
29
|
file('motion/address_book/ios/picker.rb').uses_framework('AddressBookUI')
|
22
30
|
end
|
@@ -2,57 +2,149 @@ module AddressBook
|
|
2
2
|
class AddrBook
|
3
3
|
attr_reader :ab
|
4
4
|
|
5
|
-
def initialize
|
6
|
-
@ab =
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
5
|
+
def initialize(&block)
|
6
|
+
@ab = NullAddrBook
|
7
|
+
if authorized?
|
8
|
+
activate!
|
9
|
+
if block_given?
|
10
|
+
yield self
|
11
|
+
end
|
12
|
+
elsif block
|
13
|
+
# asynchronous auth
|
14
|
+
AddressBook.request_authorization do |granted|
|
15
|
+
if granted
|
16
|
+
activate!
|
17
|
+
block.call(self)
|
18
|
+
end
|
12
19
|
end
|
13
20
|
else
|
14
|
-
|
15
|
-
|
21
|
+
# synchronous auth
|
22
|
+
if native_ab = AddressBook.address_book
|
23
|
+
@ab = LiveAddrBook.new(native_ab)
|
16
24
|
end
|
17
25
|
end
|
18
26
|
end
|
27
|
+
|
28
|
+
def activate!
|
29
|
+
@ab = LiveAddrBook.new(AddressBook.address_book)
|
30
|
+
end
|
31
|
+
def authorized?
|
32
|
+
AddressBook.authorized?
|
33
|
+
end
|
34
|
+
|
35
|
+
def auth!
|
36
|
+
raise "iOS Address Book authorization is required." if @ab.nil?
|
37
|
+
end
|
38
|
+
|
39
|
+
def people(opts = {}, &block)
|
40
|
+
ordered_list = ab_people(opts).map { |p| Person.new(nil, p, address_book: ab.ab) }
|
41
|
+
if block
|
42
|
+
ordered_list.sort_by { |p| block.call(p) }
|
43
|
+
else
|
44
|
+
ordered_list
|
45
|
+
end
|
46
|
+
end
|
19
47
|
def count
|
20
|
-
|
48
|
+
ab.person_count
|
21
49
|
end
|
22
50
|
def new_person(attributes)
|
23
|
-
Person.new(attributes, nil, :address_book =>
|
51
|
+
Person.new(attributes, nil, :address_book => ab.ab)
|
24
52
|
end
|
25
53
|
def create_person(attributes)
|
26
|
-
p =
|
54
|
+
p = new_person(attributes)
|
27
55
|
p.save
|
28
56
|
p
|
29
57
|
end
|
30
58
|
def person(id)
|
31
|
-
(p =
|
59
|
+
(p = ab.person_with_id(id)) && Person.new(nil, p, :address_book => ab.ab)
|
32
60
|
end
|
33
61
|
def changedSince(timestamp)
|
34
62
|
people.select {|p| p.modification_date > timestamp}
|
35
63
|
end
|
36
64
|
|
37
65
|
def groups
|
38
|
-
|
39
|
-
AddressBook::Group.new(:ab_group => ab_group, :address_book =>
|
66
|
+
ab.all_groups.map do |ab_group|
|
67
|
+
AddressBook::Group.new(:ab_group => ab_group, :address_book => ab.ab)
|
40
68
|
end
|
41
69
|
end
|
70
|
+
def group_count
|
71
|
+
ab.group_count
|
72
|
+
end
|
42
73
|
def new_group(attributes)
|
43
|
-
AddressBook::Group.new(:attributes => attributes, :address_book =>
|
74
|
+
AddressBook::Group.new(:attributes => attributes, :address_book => ab.ab)
|
44
75
|
end
|
45
76
|
def group(id)
|
46
|
-
(g =
|
77
|
+
(g = ab.group_with_id(id)) && Group.new(:ab_group => g, :address_book => ab.ab)
|
47
78
|
end
|
48
79
|
|
49
80
|
def notify_changes(callback, context)
|
50
|
-
|
81
|
+
ab.register_callback(callback, context)
|
82
|
+
end
|
83
|
+
|
84
|
+
def sources
|
85
|
+
ab.sources.map {|s| Source.new(s)}
|
86
|
+
end
|
87
|
+
|
88
|
+
def inspect
|
89
|
+
"#<#{self.class}:#{"0x%0x" % object_id} #{ab.status}>"
|
90
|
+
end
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
def ab_people(opts = {})
|
95
|
+
ab_source = opts[:source]
|
96
|
+
ordering = opts.fetch(:ordering) { ABPersonGetSortOrdering() }
|
97
|
+
|
98
|
+
ab_people = if ab_source
|
99
|
+
ab.all_people_in_source(ab_source)
|
100
|
+
else
|
101
|
+
ab.all_people
|
102
|
+
end
|
103
|
+
|
104
|
+
ab_people.sort! { |x, y| ABPersonComparePeopleByName(x, y, ordering) }
|
105
|
+
ab_people
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class NullAddrBook
|
110
|
+
def self.status; "pending"; end
|
111
|
+
def self.method_missing(*args)
|
112
|
+
raise SecurityError, "iOS Address Book authorization is required."
|
51
113
|
end
|
114
|
+
end
|
52
115
|
|
116
|
+
class LiveAddrBook
|
117
|
+
attr_reader :ab
|
118
|
+
def initialize(ab)
|
119
|
+
@ab = ab
|
120
|
+
end
|
121
|
+
def status; "live"; end
|
122
|
+
def all_people
|
123
|
+
ABAddressBookCopyArrayOfAllPeople(ab)
|
124
|
+
end
|
125
|
+
def all_people_in_source(source)
|
126
|
+
ABAddressBookCopyArrayOfAllPeopleInSource(ab, source)
|
127
|
+
end
|
128
|
+
def person_with_id(id)
|
129
|
+
ABAddressBookGetPersonWithRecordID(ab, id)
|
130
|
+
end
|
131
|
+
def person_count
|
132
|
+
ABAddressBookGetPersonCount(ab)
|
133
|
+
end
|
134
|
+
def all_groups
|
135
|
+
ABAddressBookCopyArrayOfAllGroups(ab)
|
136
|
+
end
|
137
|
+
def group_count
|
138
|
+
ABAddressBookGetGroupCount(ab)
|
139
|
+
end
|
140
|
+
def group_with_id(id)
|
141
|
+
ABAddressBookGetGroupWithRecordID(ab, id)
|
142
|
+
end
|
53
143
|
def sources
|
54
|
-
|
55
|
-
|
144
|
+
ABAddressBookCopyArrayOfAllSources(ab)
|
145
|
+
end
|
146
|
+
def register_callback(callback, context)
|
147
|
+
ABAddressBookRegisterExternalChangeCallback(ab, callback, context)
|
56
148
|
end
|
57
149
|
end
|
58
150
|
end
|
@@ -5,23 +5,12 @@
|
|
5
5
|
#
|
6
6
|
module AddressBook
|
7
7
|
class Group
|
8
|
-
attr_reader :attributes, :error
|
8
|
+
attr_reader :attributes, :error, :address_book
|
9
9
|
|
10
10
|
def initialize(opts)
|
11
|
-
@address_book = opts
|
12
|
-
|
13
|
-
|
14
|
-
@ab_group = opts[:ab_group]
|
15
|
-
@attributes = nil
|
16
|
-
else
|
17
|
-
# create new
|
18
|
-
@ab_group = nil
|
19
|
-
@attributes = opts[:attributes]
|
20
|
-
end
|
21
|
-
end
|
22
|
-
|
23
|
-
def address_book
|
24
|
-
@address_book ||= AddressBook.address_book
|
11
|
+
@address_book = opts.fetch(:address_book) { AddressBook.address_book }
|
12
|
+
@ab_group = opts[:ab_group]
|
13
|
+
@attributes = opts[:attributes]
|
25
14
|
end
|
26
15
|
|
27
16
|
def save
|
@@ -41,23 +30,30 @@ module AddressBook
|
|
41
30
|
unless new?
|
42
31
|
ABAddressBookRemoveRecord(address_book, ab_group, error)
|
43
32
|
ABAddressBookSave(address_book, error)
|
44
|
-
@ab_group =
|
33
|
+
@ab_group = :deleted
|
45
34
|
self
|
46
35
|
end
|
47
36
|
end
|
48
37
|
|
38
|
+
def deleted?
|
39
|
+
@ab_group == :deleted
|
40
|
+
end
|
41
|
+
|
49
42
|
def ab_group
|
50
43
|
@ab_group || convert_dict_to_ab
|
51
44
|
end
|
52
45
|
alias :ab_record :ab_group
|
53
46
|
|
54
47
|
def uid
|
55
|
-
ABRecordGetRecordID(ab_group)
|
48
|
+
deleted? ? nil : ABRecordGetRecordID(ab_group)
|
56
49
|
end
|
57
50
|
|
58
51
|
def name
|
59
52
|
ABRecordCopyValue(ab_group, KABGroupNameProperty)
|
60
53
|
end
|
54
|
+
def name=(newname)
|
55
|
+
ABRecordSetValue(ab_group, KABGroupNameProperty, newname, error)
|
56
|
+
end
|
61
57
|
|
62
58
|
def size
|
63
59
|
members.count
|
@@ -77,11 +73,30 @@ module AddressBook
|
|
77
73
|
end
|
78
74
|
end
|
79
75
|
|
76
|
+
def add(*items)
|
77
|
+
items.each { |item| self << item }
|
78
|
+
self
|
79
|
+
end
|
80
|
+
|
81
|
+
def remove(person_or_group)
|
82
|
+
ABGroupRemoveMember(ab_group, person_or_group.ab_record, error)
|
83
|
+
self
|
84
|
+
end
|
85
|
+
|
80
86
|
def <<(person_or_group)
|
81
87
|
raise ArgumentError, "Must save member before adding to group" if person_or_group.new?
|
82
88
|
ABGroupAddMember(ab_group, person_or_group.ab_record, error)
|
83
89
|
end
|
84
90
|
|
91
|
+
def to_s
|
92
|
+
if deleted?
|
93
|
+
"#<#{self.class}:DELETED>"
|
94
|
+
else
|
95
|
+
"#<#{self.class}:#{uid}:#{name}: #{size} members>"
|
96
|
+
end
|
97
|
+
end
|
98
|
+
alias :inspect :to_s
|
99
|
+
|
85
100
|
private
|
86
101
|
|
87
102
|
def convert_dict_to_ab
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module AddressBook
|
2
2
|
class MultiValued
|
3
|
+
include Enumerable
|
4
|
+
|
3
5
|
attr_reader :mv_type
|
4
6
|
|
5
7
|
def initialize(opts)
|
@@ -23,6 +25,19 @@ module AddressBook
|
|
23
25
|
def attributes
|
24
26
|
@attributes ||= convert_multi_value_into_dictionary
|
25
27
|
end
|
28
|
+
def to_ary
|
29
|
+
attributes
|
30
|
+
end
|
31
|
+
def [](index)
|
32
|
+
attributes[index]
|
33
|
+
end
|
34
|
+
def each
|
35
|
+
attributes.each { |i| yield i }
|
36
|
+
end
|
37
|
+
def to_s
|
38
|
+
"#<#{self.class}: #{attributes}>"
|
39
|
+
end
|
40
|
+
alias :inspect :to_s
|
26
41
|
|
27
42
|
def convert_multi_value_into_dictionary
|
28
43
|
count.times.map do |i|
|
@@ -122,10 +137,10 @@ module AddressBook
|
|
122
137
|
|
123
138
|
def ab_record_to_dict(i)
|
124
139
|
case multi_value_property_type
|
140
|
+
when KABDateTimePropertyType
|
141
|
+
{:date => ABMultiValueCopyValueAtIndex(@ab_multi_value, i)}
|
125
142
|
when KABStringPropertyType
|
126
143
|
{:value => ABMultiValueCopyValueAtIndex(@ab_multi_value, i)}
|
127
|
-
when KABDateTimePropertyType
|
128
|
-
{:date => ABHack.getDateValueAtIndex(i, from: @ab_multi_value)}
|
129
144
|
when KABDictionaryPropertyType
|
130
145
|
ab_record = ABMultiValueCopyValueAtIndex(@ab_multi_value, i)
|
131
146
|
PropertyMap.each_with_object({}) do |(ab_key, attr_key), dict|
|
@@ -1,28 +1,16 @@
|
|
1
1
|
module AddressBook
|
2
2
|
class Person
|
3
3
|
attr_reader :error
|
4
|
+
attr_reader :address_book
|
4
5
|
|
5
|
-
def initialize(attributes=
|
6
|
-
@address_book = opts
|
7
|
-
|
8
|
-
|
9
|
-
@attributes = attributes
|
10
|
-
else
|
11
|
-
@ab_person = existing_ab_person
|
12
|
-
@attributes = nil
|
13
|
-
end
|
6
|
+
def initialize(attributes = nil, existing_ab_person = nil, opts = {})
|
7
|
+
@address_book = opts.fetch(:address_book) { AddressBook.address_book }
|
8
|
+
@attributes = attributes
|
9
|
+
@ab_person = existing_ab_person
|
14
10
|
end
|
15
11
|
|
16
|
-
def self.all
|
17
|
-
|
18
|
-
ab_people = ABAddressBookCopyArrayOfAllPeople(ab)
|
19
|
-
return [] if ab_people.nil?
|
20
|
-
|
21
|
-
people = ab_people.map do |ab_person|
|
22
|
-
new({}, ab_person, :address_book => ab)
|
23
|
-
end
|
24
|
-
people.sort! { |a,b| "#{a.first_name} #{a.last_name}" <=> "#{b.first_name} #{b.last_name}" }
|
25
|
-
people
|
12
|
+
def self.all(options = {})
|
13
|
+
AddressBook::AddrBook.new.people(options)
|
26
14
|
end
|
27
15
|
|
28
16
|
def self.create(attributes)
|
@@ -35,7 +23,7 @@ module AddressBook
|
|
35
23
|
ABAddressBookAddRecord(address_book, ab_person, error)
|
36
24
|
ABAddressBookSave(address_book, error)
|
37
25
|
@attributes = nil # force refresh
|
38
|
-
|
26
|
+
self
|
39
27
|
end
|
40
28
|
|
41
29
|
def attributes
|
@@ -65,7 +53,7 @@ module AddressBook
|
|
65
53
|
conditions.keys.all? do |attribute|
|
66
54
|
case attribute
|
67
55
|
when :email
|
68
|
-
emails.
|
56
|
+
emails.map {|rec| rec[:value]}.any? {|v| v == conditions[attribute]}
|
69
57
|
else
|
70
58
|
send(attribute) == conditions[attribute]
|
71
59
|
end
|
@@ -215,6 +203,8 @@ module AddressBook
|
|
215
203
|
def get_multi_valued(field)
|
216
204
|
if mv = ABRecordCopyValue(ab_person, field)
|
217
205
|
MultiValued.new(:ab_multi_value => mv)
|
206
|
+
else
|
207
|
+
[]
|
218
208
|
end
|
219
209
|
end
|
220
210
|
|
@@ -223,7 +213,7 @@ module AddressBook
|
|
223
213
|
end
|
224
214
|
|
225
215
|
def phone_values
|
226
|
-
phones.
|
216
|
+
phones.map {|r| r[:value]}
|
227
217
|
end
|
228
218
|
|
229
219
|
def emails
|
@@ -231,7 +221,7 @@ module AddressBook
|
|
231
221
|
end
|
232
222
|
|
233
223
|
def email_values
|
234
|
-
emails.
|
224
|
+
emails.map {|r| r[:value]}
|
235
225
|
end
|
236
226
|
|
237
227
|
def addresses
|
@@ -260,8 +250,8 @@ module AddressBook
|
|
260
250
|
|
261
251
|
def email; email_values.first; end
|
262
252
|
def phone; phone_values.first; end
|
263
|
-
def url; urls.
|
264
|
-
def address; addresses.
|
253
|
+
def url; urls.any? && urls.first[:value]; end
|
254
|
+
def address; addresses.any? && addresses.first; end
|
265
255
|
|
266
256
|
def find_or_new
|
267
257
|
if new_record?
|
@@ -300,16 +290,11 @@ module AddressBook
|
|
300
290
|
end
|
301
291
|
|
302
292
|
def modification_date
|
303
|
-
|
304
|
-
# see http://hipbyte.myjetbrains.com/youtrack/issue/RM-81
|
305
|
-
# still broken in RubyMotion 2.0
|
306
|
-
ABHack.getDateProperty(KABPersonModificationDateProperty, from: ab_person)
|
307
|
-
# when RubyMotion bug is fixed, this should just be
|
308
|
-
# get_field(KABPersonModificationDateProperty)
|
293
|
+
get_field(KABPersonModificationDateProperty)
|
309
294
|
end
|
310
295
|
|
311
296
|
def creation_date
|
312
|
-
|
297
|
+
get_field(KABPersonCreationDateProperty)
|
313
298
|
end
|
314
299
|
|
315
300
|
# replace *all* properties of an existing Person with new values
|
@@ -427,10 +412,9 @@ module AddressBook
|
|
427
412
|
end
|
428
413
|
|
429
414
|
Person.multi_value_property_map.each do |ab_property, attr_key|
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
end
|
415
|
+
value = get_multi_valued(ab_property)
|
416
|
+
if value.any?
|
417
|
+
@attributes[attr_key] = value.attributes
|
434
418
|
end
|
435
419
|
end
|
436
420
|
|
@@ -443,13 +427,7 @@ module AddressBook
|
|
443
427
|
end
|
444
428
|
end
|
445
429
|
def get_field(field)
|
446
|
-
|
447
|
-
# special case: RubyMotion blows up on NSDate properties
|
448
|
-
# see http://hipbyte.myjetbrains.com/youtrack/issue/RM-81
|
449
|
-
ABHack.getDateProperty(field, from: ab_person)
|
450
|
-
else
|
451
|
-
ABRecordCopyValue(ab_person, field)
|
452
|
-
end
|
430
|
+
ABRecordCopyValue(ab_person, field)
|
453
431
|
end
|
454
432
|
def remove_field(field)
|
455
433
|
ABRecordRemoveValue(ab_person, field, nil)
|
@@ -475,9 +453,5 @@ module AddressBook
|
|
475
453
|
# what if there are more than one match? email should be unique but ...
|
476
454
|
existing_records.first
|
477
455
|
end
|
478
|
-
|
479
|
-
def address_book
|
480
|
-
@address_book ||= AddressBook.address_book
|
481
|
-
end
|
482
456
|
end
|
483
457
|
end
|
@@ -3,10 +3,10 @@ module AddressBook
|
|
3
3
|
class << self
|
4
4
|
attr_accessor :showing
|
5
5
|
end
|
6
|
-
def self.show(&after)
|
6
|
+
def self.show(options={}, &after)
|
7
7
|
raise "Cannot show two Pickers" if showing?
|
8
8
|
@picker = Picker.new(&after)
|
9
|
-
@picker.show
|
9
|
+
@picker.show options
|
10
10
|
@picker
|
11
11
|
end
|
12
12
|
|
@@ -18,12 +18,13 @@ module AddressBook
|
|
18
18
|
@after = after
|
19
19
|
end
|
20
20
|
|
21
|
-
def show
|
21
|
+
def show(options)
|
22
22
|
self.class.showing = true
|
23
23
|
|
24
24
|
@people_picker_ctlr = ABPeoplePickerNavigationController.alloc.init
|
25
25
|
@people_picker_ctlr.peoplePickerDelegate = self
|
26
|
-
UIApplication.sharedApplication.keyWindow.rootViewController
|
26
|
+
presenter = options.fetch :presenter, UIApplication.sharedApplication.keyWindow.rootViewController
|
27
|
+
presenter.presentViewController(@people_picker_ctlr, animated:true, completion:nil)
|
27
28
|
end
|
28
29
|
|
29
30
|
def hide(ab_person=nil)
|
@@ -53,8 +54,8 @@ end
|
|
53
54
|
|
54
55
|
module AddressBook
|
55
56
|
module_function
|
56
|
-
def pick(&after)
|
57
|
-
AddressBook::Picker.show &after
|
57
|
+
def pick(options={}, &after)
|
58
|
+
AddressBook::Picker.show options, &after
|
58
59
|
end
|
59
60
|
end
|
60
61
|
|
@@ -46,6 +46,9 @@ module AddressBook
|
|
46
46
|
AddressBook::Group.new(:ab_group => ab_group, :address_book => @ab)
|
47
47
|
end
|
48
48
|
end
|
49
|
+
def group_count
|
50
|
+
groups.count
|
51
|
+
end
|
49
52
|
def new_group(attributes)
|
50
53
|
AddressBook::Group.new(:attributes => attributes, :address_book => @ab)
|
51
54
|
end
|
@@ -56,12 +59,13 @@ module AddressBook
|
|
56
59
|
end
|
57
60
|
|
58
61
|
def notify_changes(callback, context)
|
59
|
-
|
62
|
+
App.notification_center.observe KABDatabaseChangedExternallyNotification do |notification|
|
63
|
+
callback.call(context)
|
64
|
+
end
|
60
65
|
end
|
61
66
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
# end
|
67
|
+
def inspect
|
68
|
+
"#<#{self.class}:#{"0x%0x" % object_id} #{count} people, #{group_count} groups>"
|
69
|
+
end
|
66
70
|
end
|
67
71
|
end
|
@@ -6,9 +6,10 @@
|
|
6
6
|
module AddressBook
|
7
7
|
class Group
|
8
8
|
attr_reader :attributes, :error
|
9
|
+
attr_reader :address_book
|
9
10
|
|
10
11
|
def initialize(opts)
|
11
|
-
@address_book = opts
|
12
|
+
@address_book = opts.fetch(:address_book) { AddressBook.address_book }
|
12
13
|
if opts[:ab_group]
|
13
14
|
# import existing
|
14
15
|
@ab_group = opts[:ab_group]
|
@@ -20,10 +21,6 @@ module AddressBook
|
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
23
|
-
def address_book
|
24
|
-
@address_book ||= AddressBook.address_book
|
25
|
-
end
|
26
|
-
|
27
24
|
def save
|
28
25
|
address_book.addRecord(ab_group)
|
29
26
|
address_book.save
|
@@ -64,6 +61,9 @@ module AddressBook
|
|
64
61
|
def name
|
65
62
|
get_field(KABGroupNameProperty)
|
66
63
|
end
|
64
|
+
def name=(newname)
|
65
|
+
ABRecordSetValue(ab_group, KABGroupNameProperty, newname)
|
66
|
+
end
|
67
67
|
|
68
68
|
def size
|
69
69
|
members.count
|
@@ -85,7 +85,15 @@ module AddressBook
|
|
85
85
|
|
86
86
|
def <<(person_or_group)
|
87
87
|
raise ArgumentError, "Must save member before adding to group" if person_or_group.new?
|
88
|
-
ABGroupAddMember(ab_group, person_or_group.ab_record
|
88
|
+
ABGroupAddMember(ab_group, person_or_group.ab_record)
|
89
|
+
end
|
90
|
+
def add(*items)
|
91
|
+
items.each { |item| self << item }
|
92
|
+
self
|
93
|
+
end
|
94
|
+
def remove(person_or_group)
|
95
|
+
ABGroupRemoveMember(ab_group, person_or_group.ab_record)
|
96
|
+
self
|
89
97
|
end
|
90
98
|
|
91
99
|
def local?
|
@@ -1,5 +1,7 @@
|
|
1
1
|
module AddressBook
|
2
2
|
class MultiValued
|
3
|
+
include Enumerable
|
4
|
+
|
3
5
|
attr_reader :mv_type
|
4
6
|
|
5
7
|
def initialize(opts)
|
@@ -23,6 +25,19 @@ module AddressBook
|
|
23
25
|
def attributes
|
24
26
|
@attributes ||= convert_multi_value_into_dictionary
|
25
27
|
end
|
28
|
+
def to_ary
|
29
|
+
attributes
|
30
|
+
end
|
31
|
+
def [](index)
|
32
|
+
attributes[index]
|
33
|
+
end
|
34
|
+
def each
|
35
|
+
attributes.each { |i| yield i }
|
36
|
+
end
|
37
|
+
def to_s
|
38
|
+
"#<#{self.class}: #{attributes}>"
|
39
|
+
end
|
40
|
+
alias :inspect :to_s
|
26
41
|
|
27
42
|
def convert_multi_value_into_dictionary
|
28
43
|
count.times.map do |i|
|
@@ -1,9 +1,10 @@
|
|
1
1
|
module AddressBook
|
2
2
|
class Person
|
3
3
|
attr_reader :error
|
4
|
+
attr_reader :address_book
|
4
5
|
|
5
6
|
def initialize(target, opts = {})
|
6
|
-
@address_book = opts
|
7
|
+
@address_book = opts.fetch(:address_book) { AddressBook.address_book }
|
7
8
|
if target.respond_to?(:fetch)
|
8
9
|
# data for a new Person, to be saved to the Address Book
|
9
10
|
@ab_person = nil
|
@@ -41,13 +42,17 @@ module AddressBook
|
|
41
42
|
get_field('com.apple.uuid')
|
42
43
|
end
|
43
44
|
|
44
|
-
def method_missing(
|
45
|
-
if property = ReverseSingleValuePropertyMap[
|
45
|
+
def method_missing(method_name, *args)
|
46
|
+
if property = ReverseSingleValuePropertyMap[method_name]
|
46
47
|
get_field(property)
|
48
|
+
elsif property = ReverseSingleValuePropertyMap[method_name.gsub(/=$/, '').to_sym]
|
49
|
+
set_field(property, args.first)
|
50
|
+
@attributes = nil
|
47
51
|
else
|
48
52
|
super
|
49
53
|
end
|
50
54
|
end
|
55
|
+
|
51
56
|
# def self.method_missing(name, *args)
|
52
57
|
# if attribute_name = all_finder?(name)
|
53
58
|
# find_all_by(attribute_name, args.first)
|
@@ -107,6 +112,8 @@ module AddressBook
|
|
107
112
|
def get_multi_valued(field)
|
108
113
|
if mv = get_field(field)
|
109
114
|
MultiValued.new(:ab_multi_value => mv)
|
115
|
+
else
|
116
|
+
[]
|
110
117
|
end
|
111
118
|
end
|
112
119
|
|
@@ -240,7 +247,7 @@ module AddressBook
|
|
240
247
|
KABFirstNameProperty => :first_name,
|
241
248
|
KABLastNameProperty => :last_name,
|
242
249
|
KABMiddleNameProperty => :middle_name,
|
243
|
-
|
250
|
+
KABTitleProperty => :prefix,
|
244
251
|
KABSuffixProperty => :suffix,
|
245
252
|
KABNicknameProperty => :nickname,
|
246
253
|
KABJobTitleProperty => :job_title,
|
@@ -314,7 +321,7 @@ module AddressBook
|
|
314
321
|
|
315
322
|
MultiValuePropertyMap.each do |ab_property, attr_key|
|
316
323
|
if value = get_multi_valued(ab_property)
|
317
|
-
if value.
|
324
|
+
if value.any?
|
318
325
|
@attributes[attr_key] = value.attributes
|
319
326
|
end
|
320
327
|
end
|
@@ -342,9 +349,5 @@ module AddressBook
|
|
342
349
|
set_field(field, multi_field.ab_multi_value)
|
343
350
|
end
|
344
351
|
end
|
345
|
-
|
346
|
-
def address_book
|
347
|
-
@address_book ||= AddressBook.address_book
|
348
|
-
end
|
349
352
|
end
|
350
353
|
end
|
data/motion/address_book.rb
CHANGED
@@ -5,10 +5,10 @@ module AddressBook
|
|
5
5
|
if App.osx?
|
6
6
|
ABAddressBook.addressBook
|
7
7
|
else # iOS
|
8
|
-
if Device.ios_version
|
9
|
-
ios6_create
|
10
|
-
else
|
8
|
+
if Device.ios_version =~ /5/
|
11
9
|
ios5_create
|
10
|
+
else
|
11
|
+
ios6_create
|
12
12
|
end
|
13
13
|
end
|
14
14
|
end
|
@@ -23,8 +23,13 @@ module AddressBook
|
|
23
23
|
|
24
24
|
def ios6_create
|
25
25
|
error = nil
|
26
|
-
|
27
|
-
|
26
|
+
if authorized?
|
27
|
+
@address_book = ABAddressBookCreateWithOptions(nil, error)
|
28
|
+
else
|
29
|
+
request_authorization do |rc|
|
30
|
+
NSLog "AddressBook: access was #{rc ? 'approved' : 'denied'}"
|
31
|
+
end
|
32
|
+
end
|
28
33
|
@address_book
|
29
34
|
end
|
30
35
|
|
@@ -24,6 +24,10 @@ describe AddressBook::Group do
|
|
24
24
|
@group.should.not.be.new
|
25
25
|
end
|
26
26
|
|
27
|
+
it "should describe itself" do
|
28
|
+
@group.to_s.should.match /AddressBook::Group:.*:Test Group: 0 members/
|
29
|
+
end
|
30
|
+
|
27
31
|
it "should not add unsaved person records" do
|
28
32
|
p = @ab.new_person({:first_name => 'Alice', :last_name => 'Artichoke'})
|
29
33
|
lambda {@group << p}.should.raise ArgumentError
|
@@ -32,13 +36,10 @@ describe AddressBook::Group do
|
|
32
36
|
|
33
37
|
describe 'a group with members' do
|
34
38
|
before do
|
35
|
-
@p1 = @ab.
|
36
|
-
@
|
37
|
-
@p2 = @ab.new_person({:first_name => 'Bob', :emails => [{:label => 'home', :value => 'bob@example.com'}]})
|
38
|
-
@p2.save
|
39
|
+
@p1 = @ab.create_person({:first_name => 'Alice', :emails => [{:label => 'home', :value => 'alice@example.com'}]})
|
40
|
+
@p2 = @ab.create_person({:first_name => 'Bob', :emails => [{:label => 'home', :value => 'bob@example.com'}]})
|
39
41
|
@group = @ab.new_group(:name => 'Test Group')
|
40
|
-
@group
|
41
|
-
@group << @p2
|
42
|
+
@group.add(@p1, @p2)
|
42
43
|
@group.save
|
43
44
|
end
|
44
45
|
after do
|
@@ -58,6 +59,45 @@ describe AddressBook::Group do
|
|
58
59
|
it "should search by id" do
|
59
60
|
@ab.group(@group.uid).name.should.equal @group.name
|
60
61
|
end
|
62
|
+
|
63
|
+
it "should describe itself" do
|
64
|
+
@group.to_s.should.match /AddressBook::Group:.*:Test Group: 2 members/
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "after removing one member" do
|
68
|
+
before do
|
69
|
+
@group.remove(@p2)
|
70
|
+
@group.save
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should no longer contain the removed member" do
|
74
|
+
@group.members.map(&:uid).should.equal [@p1.uid]
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe 'a deleted group' do
|
80
|
+
before do
|
81
|
+
@p1 = @ab.create_person({:first_name => 'Alice', :emails => [{:label => 'home', :value => 'alice@example.com'}]})
|
82
|
+
@p2 = @ab.create_person({:first_name => 'Bob', :emails => [{:label => 'home', :value => 'bob@example.com'}]})
|
83
|
+
@group = @ab.new_group(:name => 'Test Group')
|
84
|
+
@group.add(@p1, @p2)
|
85
|
+
@group.save
|
86
|
+
@group.delete!
|
87
|
+
end
|
88
|
+
|
89
|
+
after do
|
90
|
+
@p1.delete!
|
91
|
+
@p2.delete!
|
92
|
+
end
|
93
|
+
|
94
|
+
it "should have no uid" do
|
95
|
+
@group.uid.should.be.nil
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should describe itself" do
|
99
|
+
@group.to_s.should.match /AddressBook::Group:DELETED/
|
100
|
+
end
|
61
101
|
end
|
62
102
|
|
63
103
|
# TODO: nested groups
|
@@ -1,4 +1,12 @@
|
|
1
1
|
describe AddressBook::Person do
|
2
|
+
before do
|
3
|
+
@ab = AddressBook::AddrBook.new
|
4
|
+
end
|
5
|
+
|
6
|
+
it "should be authorized" do
|
7
|
+
@ab.authorized?.should.be.true
|
8
|
+
end
|
9
|
+
|
2
10
|
describe 'ways of creating and finding people' do
|
3
11
|
describe 'new' do
|
4
12
|
before do
|
@@ -157,7 +165,11 @@ describe AddressBook::Person do
|
|
157
165
|
{ :label => 'home page', :value => "http://www.mysite.com/" },
|
158
166
|
{ :label => 'work', :value => 'http://dept.bigco.com/' },
|
159
167
|
{ :label => 'school', :value => 'http://state.edu/college' }
|
160
|
-
]
|
168
|
+
],
|
169
|
+
:social_profiles => [
|
170
|
+
{ service: 'FaceBook', username: 'testyman' },
|
171
|
+
{ service: 'twitter', username: 'testwit' }
|
172
|
+
]
|
161
173
|
}
|
162
174
|
end
|
163
175
|
|
@@ -242,6 +254,11 @@ describe AddressBook::Person do
|
|
242
254
|
@ab_person.urls.attributes.should.equal @attributes[:urls]
|
243
255
|
end
|
244
256
|
|
257
|
+
it 'should round-trip social profiles' do
|
258
|
+
@ab_person.social_profiles.count.should.equal 2
|
259
|
+
@ab_person.attributes[:social_profiles].should.equal @attributes[:social_profiles]
|
260
|
+
end
|
261
|
+
|
245
262
|
describe 'once saved' do
|
246
263
|
before do
|
247
264
|
@before_count = AddressBook.count
|
@@ -446,4 +463,35 @@ describe AddressBook::Person do
|
|
446
463
|
end
|
447
464
|
end
|
448
465
|
end
|
466
|
+
|
467
|
+
describe "sorting" do
|
468
|
+
before do
|
469
|
+
@ab.people.each(&:delete!)
|
470
|
+
|
471
|
+
@p1 = @ab.create_person({:first_name => 'Bob', :last_name => 'Edwards'}).uid
|
472
|
+
@p2 = @ab.create_person({:first_name => 'Doris', :last_name => 'Channing'}).uid
|
473
|
+
@p3 = @ab.create_person({:first_name => 'Anne', :last_name => 'Brown'}).uid
|
474
|
+
@p4 = @ab.create_person({:first_name => 'Eddie', :last_name => 'Anderson'}).uid
|
475
|
+
@p5 = @ab.create_person({:first_name => 'Carol', :last_name => 'Dolittle'}).uid
|
476
|
+
end
|
477
|
+
|
478
|
+
it "should sort on last name using OS sort" do
|
479
|
+
@ab.people(ordering: KABPersonSortByLastName).map(&:uid).should.equal [@p4, @p3, @p2, @p5, @p1]
|
480
|
+
end
|
481
|
+
it "should support last-name sort in Person#all" do
|
482
|
+
AddressBook::Person.all(ordering: KABPersonSortByLastName).map(&:uid).should.equal [@p4, @p3, @p2, @p5, @p1]
|
483
|
+
end
|
484
|
+
|
485
|
+
it "should sort on first name using OS sort" do
|
486
|
+
@ab.people(ordering: KABPersonSortByFirstName).map(&:uid).should.equal [@p3, @p1, @p5, @p2, @p4]
|
487
|
+
end
|
488
|
+
it "should support first-name sort in Person#all" do
|
489
|
+
AddressBook::Person.all(ordering: KABPersonSortByFirstName).map(&:uid).should.equal [@p3, @p1, @p5, @p2, @p4]
|
490
|
+
end
|
491
|
+
|
492
|
+
it "should support a custom sort order" do
|
493
|
+
ordered = @ab.people { |p| p.last_name[1] }.map(&:uid)
|
494
|
+
ordered.should.equal [@p1, @p2, @p4, @p5, @p3]
|
495
|
+
end
|
496
|
+
end
|
449
497
|
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# These methods are intended to protect contents of the simulator
|
2
|
+
# instance from destruction when the spec suite runs. Specifically,
|
3
|
+
# the contents of the iOS Address Book, which is global to all
|
4
|
+
# applications.
|
5
|
+
|
6
|
+
SIMULATOR_ROOT = "#{ENV['HOME']}/../.."
|
7
|
+
AB_PATH = SIMULATOR_ROOT + "/Library/AddressBook"
|
8
|
+
AB_PATH_BAK = AB_PATH + ".bak"
|
9
|
+
|
10
|
+
def protect_existing_address_book
|
11
|
+
# Can't use ruby methods to operate on these directories because the
|
12
|
+
# iOS layer protects them, but shelling out is still allowed so we
|
13
|
+
# can alter the world that way.
|
14
|
+
|
15
|
+
unless AddressBook.authorized?
|
16
|
+
warn "BLOCKING FOR AUTHORIZATION"
|
17
|
+
AddressBook.request_authorization
|
18
|
+
end
|
19
|
+
|
20
|
+
warn "PROTECTING EXISTING ADDRESS BOOK IN SIMULATOR"
|
21
|
+
|
22
|
+
`mv \"#{AB_PATH}\" "#{AB_PATH_BAK}"`
|
23
|
+
end
|
24
|
+
|
25
|
+
at_exit do
|
26
|
+
warn "RESTORING ORIGINAL ADDRESS BOOK IN SIMULATOR"
|
27
|
+
|
28
|
+
`rm -rf \"#{AB_PATH}\"`
|
29
|
+
`mv \"#{AB_PATH_BAK}\" "#{AB_PATH}"`
|
30
|
+
end
|
31
|
+
|
32
|
+
protect_existing_address_book
|
@@ -41,16 +41,16 @@ describe AddressBook::Person do
|
|
41
41
|
end
|
42
42
|
|
43
43
|
it 'should be able to get each of the single value fields' do
|
44
|
-
@the_person.first_name.should.equal @alex_data[:first_name
|
45
|
-
@the_person.last_name.should.equal @alex_data[:last_name
|
46
|
-
@the_person.middle_name.should.equal
|
47
|
-
@the_person.
|
48
|
-
@the_person.suffix.should.equal
|
49
|
-
@the_person.nickname.should.equal
|
50
|
-
@the_person.job_title.should.equal @alex_data[:job_title
|
51
|
-
@the_person.department.should.equal @alex_data[:department
|
44
|
+
@the_person.first_name.should.equal @alex_data[:first_name]
|
45
|
+
@the_person.last_name.should.equal @alex_data[:last_name]
|
46
|
+
@the_person.middle_name.should.equal @alex_data[:middle_name]
|
47
|
+
@the_person.prefix.should.equal @alex_data[:prefix]
|
48
|
+
@the_person.suffix.should.equal @alex_data[:suffix]
|
49
|
+
@the_person.nickname.should.equal @alex_data[:nickname]
|
50
|
+
@the_person.job_title.should.equal @alex_data[:job_title]
|
51
|
+
@the_person.department.should.equal @alex_data[:department]
|
52
52
|
@the_person.organization.should.equal @alex_data[:organization]
|
53
|
-
@the_person.note.should.equal
|
53
|
+
@the_person.note.should.equal @alex_data[:note]
|
54
54
|
@the_person.should.be.person?
|
55
55
|
end
|
56
56
|
|
metadata
CHANGED
@@ -1,8 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-addressbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
5
|
-
prerelease:
|
4
|
+
version: 1.6.2
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Alex Rothenberg
|
@@ -10,12 +9,11 @@ authors:
|
|
10
9
|
autorequire:
|
11
10
|
bindir: bin
|
12
11
|
cert_chain: []
|
13
|
-
date: 2013-
|
12
|
+
date: 2013-10-22 00:00:00.000000000 Z
|
14
13
|
dependencies:
|
15
14
|
- !ruby/object:Gem::Dependency
|
16
15
|
name: bubble-wrap
|
17
16
|
requirement: !ruby/object:Gem::Requirement
|
18
|
-
none: false
|
19
17
|
requirements:
|
20
18
|
- - ~>
|
21
19
|
- !ruby/object:Gem::Version
|
@@ -23,7 +21,6 @@ dependencies:
|
|
23
21
|
type: :runtime
|
24
22
|
prerelease: false
|
25
23
|
version_requirements: !ruby/object:Gem::Requirement
|
26
|
-
none: false
|
27
24
|
requirements:
|
28
25
|
- - ~>
|
29
26
|
- !ruby/object:Gem::Version
|
@@ -31,33 +28,29 @@ dependencies:
|
|
31
28
|
- !ruby/object:Gem::Dependency
|
32
29
|
name: rake
|
33
30
|
requirement: !ruby/object:Gem::Requirement
|
34
|
-
none: false
|
35
31
|
requirements:
|
36
|
-
- -
|
32
|
+
- - '>='
|
37
33
|
- !ruby/object:Gem::Version
|
38
34
|
version: '0'
|
39
35
|
type: :development
|
40
36
|
prerelease: false
|
41
37
|
version_requirements: !ruby/object:Gem::Requirement
|
42
|
-
none: false
|
43
38
|
requirements:
|
44
|
-
- -
|
39
|
+
- - '>='
|
45
40
|
- !ruby/object:Gem::Version
|
46
41
|
version: '0'
|
47
42
|
- !ruby/object:Gem::Dependency
|
48
43
|
name: rspec
|
49
44
|
requirement: !ruby/object:Gem::Requirement
|
50
|
-
none: false
|
51
45
|
requirements:
|
52
|
-
- -
|
46
|
+
- - '>='
|
53
47
|
- !ruby/object:Gem::Version
|
54
48
|
version: '0'
|
55
49
|
type: :development
|
56
50
|
prerelease: false
|
57
51
|
version_requirements: !ruby/object:Gem::Requirement
|
58
|
-
none: false
|
59
52
|
requirements:
|
60
|
-
- -
|
53
|
+
- - '>='
|
61
54
|
- !ruby/object:Gem::Version
|
62
55
|
version: '0'
|
63
56
|
description: A RubyMotion wrapper around the iOS & OSX Address Book frameworks
|
@@ -74,8 +67,6 @@ files:
|
|
74
67
|
- LICENSE
|
75
68
|
- README.md
|
76
69
|
- Rakefile
|
77
|
-
- abhack/abhack.h
|
78
|
-
- abhack/abhack.m
|
79
70
|
- lib/motion-addressbook.rb
|
80
71
|
- lib/motion-addressbook/version.rb
|
81
72
|
- motion-addressbook.gemspec
|
@@ -98,39 +89,33 @@ files:
|
|
98
89
|
- spec/ios/helpers/bacon_matchers.rb
|
99
90
|
- spec/ios/helpers/hacks.rb
|
100
91
|
- spec/ios/helpers/person_helpers.rb
|
92
|
+
- spec/ios/helpers/spec_helper.rb
|
101
93
|
- spec/osx/address_book/person_spec.rb
|
102
94
|
- spec/osx/helpers/bacon_matchers.rb
|
103
95
|
- spec/osx/helpers/hacks.rb
|
104
96
|
- spec/osx/helpers/person_helpers.rb
|
105
97
|
homepage: ''
|
106
98
|
licenses: []
|
99
|
+
metadata: {}
|
107
100
|
post_install_message:
|
108
101
|
rdoc_options: []
|
109
102
|
require_paths:
|
110
103
|
- lib
|
111
104
|
required_ruby_version: !ruby/object:Gem::Requirement
|
112
|
-
none: false
|
113
105
|
requirements:
|
114
|
-
- -
|
106
|
+
- - '>='
|
115
107
|
- !ruby/object:Gem::Version
|
116
108
|
version: '0'
|
117
|
-
segments:
|
118
|
-
- 0
|
119
|
-
hash: -2657752188704794631
|
120
109
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
121
|
-
none: false
|
122
110
|
requirements:
|
123
|
-
- -
|
111
|
+
- - '>='
|
124
112
|
- !ruby/object:Gem::Version
|
125
113
|
version: '0'
|
126
|
-
segments:
|
127
|
-
- 0
|
128
|
-
hash: -2657752188704794631
|
129
114
|
requirements: []
|
130
115
|
rubyforge_project:
|
131
|
-
rubygems_version:
|
116
|
+
rubygems_version: 2.0.5
|
132
117
|
signing_key:
|
133
|
-
specification_version:
|
118
|
+
specification_version: 4
|
134
119
|
summary: A RubyMotion wrapper around the iOS & OSX Address Book frameworks
|
135
120
|
test_files:
|
136
121
|
- spec/ios/address_book/group_spec.rb
|
@@ -140,6 +125,7 @@ test_files:
|
|
140
125
|
- spec/ios/helpers/bacon_matchers.rb
|
141
126
|
- spec/ios/helpers/hacks.rb
|
142
127
|
- spec/ios/helpers/person_helpers.rb
|
128
|
+
- spec/ios/helpers/spec_helper.rb
|
143
129
|
- spec/osx/address_book/person_spec.rb
|
144
130
|
- spec/osx/helpers/bacon_matchers.rb
|
145
131
|
- spec/osx/helpers/hacks.rb
|
data/abhack/abhack.h
DELETED
data/abhack/abhack.m
DELETED
@@ -1,15 +0,0 @@
|
|
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
|