motion-addressbook 1.5.0 → 1.6.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
[![Build Status](https://secure.travis-ci.org/alexrothenberg/motion-addressbook.png)](http://travis-ci.org/alexrothenberg/motion-addressbook)
|
4
|
+
[![Code Climate](https://codeclimate.com/github/alexrothenberg/motion-addressbook.png)](https://codeclimate.com/github/alexrothenberg/motion-addressbook)
|
5
|
+
[![Gem Version](https://badge.fury.io/rb/motion-addressbook.png)](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
|