motion-addressbook 1.7.0 → 1.7.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -1
- data/README.md +15 -3
- data/Rakefile +46 -4
- data/app/app_delegate.rb +25 -0
- data/lib/motion-addressbook.rb +3 -0
- data/lib/motion-addressbook/version.rb +1 -1
- data/motion/address_book/ios/addr_book.rb +15 -1
- data/motion/address_book/ios/creator.rb +47 -0
- data/motion/address_book/ios/multi_valued.rb +9 -12
- data/motion/address_book/ios/person.rb +1 -1
- data/motion/address_book/ios/picker.rb +12 -16
- data/spec/ios/address_book/creator_spec.rb +33 -0
- data/spec/ios/address_book/person_spec.rb +73 -34
- data/spec/ios/address_book/picker_spec.rb +33 -31
- data/spec/ios/helpers/spec_helper.rb +25 -46
- metadata +17 -14
- data/motion/address_book/ios/multi_value.rb +0 -120
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 686d48dc6904f9b9a55304536a80a6a54ba98609
|
4
|
+
data.tar.gz: a8c743b34dc3ec02f54d5bcc80110eba64b698bf
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5b62d28e2ac4e801430d98ebb9dcd77f48dd4ce193d6966eb668ae6fa25effa893b3eba5c3c33db4da5b2fde48eabc24d22ce9f92507a5cc9c55a0567b3c6d66
|
7
|
+
data.tar.gz: 028c26080e06d2da83637864311c1130ea4b0399e378df4eb724b60c3097a061ecea3ba4bc2620338fe19ca652d682f73545c447254ab88bdf99204c7057faa4
|
data/.gitignore
CHANGED
data/README.md
CHANGED
@@ -86,16 +86,16 @@ end
|
|
86
86
|
The iOS6 simulator does not demand AddressBook authorization. The iOS7
|
87
87
|
simulator does.
|
88
88
|
|
89
|
-
### Showing the
|
89
|
+
### Showing the ABPeoplePickerNavigationController
|
90
90
|
|
91
91
|
```ruby
|
92
|
-
AddressBook.pick
|
92
|
+
AddressBook.pick do |person|
|
93
93
|
if person
|
94
94
|
# person is an AddressBook::Person object
|
95
95
|
else
|
96
96
|
# canceled
|
97
97
|
end
|
98
|
-
|
98
|
+
end
|
99
99
|
```
|
100
100
|
|
101
101
|
You can also specify the presenting controller:
|
@@ -106,6 +106,18 @@ AddressBook.pick presenter: self do |person|
|
|
106
106
|
end
|
107
107
|
```
|
108
108
|
|
109
|
+
### Showing the ABNewPersonViewController
|
110
|
+
|
111
|
+
```ruby
|
112
|
+
AddressBook.create do |person|
|
113
|
+
if person
|
114
|
+
# person is an AddressBook::Person object
|
115
|
+
else
|
116
|
+
# canceled
|
117
|
+
end
|
118
|
+
end
|
119
|
+
```
|
120
|
+
|
109
121
|
### Working with Person objects
|
110
122
|
|
111
123
|
Get a list of existing people from the Address Book. On IOS, results
|
data/Rakefile
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
2
|
$:.unshift("/Library/RubyMotion/lib")
|
3
|
+
|
3
4
|
if ENV['osx']
|
4
5
|
require 'motion/project/template/osx'
|
5
6
|
else
|
6
7
|
require 'motion/project/template/ios'
|
7
8
|
end
|
9
|
+
|
8
10
|
Bundler.setup
|
9
11
|
Bundler.require
|
10
12
|
|
11
|
-
|
12
|
-
# iOS needs an AppDelegate for REPL to launch; steal one from BW
|
13
|
-
require 'bubble-wrap/test'
|
14
|
-
end
|
13
|
+
require 'bubble-wrap/reactor'
|
15
14
|
|
16
15
|
Motion::Project::App.setup do |app|
|
17
16
|
# Use `rake config' to see complete project settings.
|
@@ -22,4 +21,47 @@ Motion::Project::App.setup do |app|
|
|
22
21
|
else
|
23
22
|
app.specs_dir = "./spec/ios"
|
24
23
|
end
|
24
|
+
|
25
|
+
if ENV['osx']
|
26
|
+
app.info_plist['LSUIElement'] = true
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# The test suite may interfere with contacts already created in the
|
31
|
+
# simulator. In order to avoid disrupting anything the existing
|
32
|
+
# simulator environment, run the test suite in its own blank simulator
|
33
|
+
# and clean up afterwards.
|
34
|
+
|
35
|
+
namespace :spec do
|
36
|
+
task :isolate do
|
37
|
+
system "launchctl list | grep simulator | cut -f3 | xargs -L 1 launchctl remove"
|
38
|
+
|
39
|
+
@_protected = []
|
40
|
+
Dir.glob("#{ENV['HOME']}/Library/Application Support/iPhone Simulator/[0-9]*").each do |dir|
|
41
|
+
if Dir.exists?("#{dir}.backup")
|
42
|
+
warn "*" * 70
|
43
|
+
warn "PREVIOUS TEST RUN FAILED. RESTORING SIMULATOR BACKUP AND ABORTING."
|
44
|
+
warn "*" * 70
|
45
|
+
system "rm -rf \"#{dir}\""
|
46
|
+
File.rename("#{dir}.backup", dir)
|
47
|
+
exit 1
|
48
|
+
else
|
49
|
+
warn "PROTECTING EXISTING SIMULATOR IN #{dir}"
|
50
|
+
File.rename(dir, "#{dir}.backup")
|
51
|
+
@_protected << dir
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
at_exit do
|
56
|
+
system "launchctl list | grep simulator | cut -f3 | head -1 | xargs launchctl remove"
|
57
|
+
|
58
|
+
@_protected.each do |dir|
|
59
|
+
warn "RESTORING SIMULATOR IN #{dir}"
|
60
|
+
system "rm -rf \"#{dir}\""
|
61
|
+
File.rename("#{dir}.backup", dir)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
task :simulator => :isolate
|
25
67
|
end
|
data/app/app_delegate.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
class AppDelegate
|
2
|
+
def application(application, didFinishLaunchingWithOptions:launchOptions)
|
3
|
+
@window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)
|
4
|
+
@window.rootViewController = UIViewController.alloc.init
|
5
|
+
@window.makeKeyAndVisible
|
6
|
+
|
7
|
+
command_line
|
8
|
+
|
9
|
+
true
|
10
|
+
end
|
11
|
+
|
12
|
+
def command_line
|
13
|
+
if command = NSProcessInfo.processInfo.environment['dump']
|
14
|
+
warn "Executing command line instruction: #{command}"
|
15
|
+
AddressBook::AddrBook.new do |ab|
|
16
|
+
case command
|
17
|
+
when 'people'
|
18
|
+
puts BW::JSON.generate(ab.people.map(&:attributes))
|
19
|
+
when 'groups'
|
20
|
+
puts BW::JSON.generate(ab.groups.map { |g| {name: g.name, members: g.members.map(&:uid) }})
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/motion-addressbook.rb
CHANGED
@@ -28,6 +28,9 @@ BubbleWrap.require_ios do
|
|
28
28
|
BW.require 'motion/address_book/ios/picker.rb' do
|
29
29
|
file('motion/address_book/ios/picker.rb').uses_framework('AddressBookUI')
|
30
30
|
end
|
31
|
+
BW.require 'motion/address_book/ios/creator.rb' do
|
32
|
+
file('motion/address_book/ios/creator.rb').uses_framework('AddressBookUI')
|
33
|
+
end
|
31
34
|
end
|
32
35
|
|
33
36
|
BubbleWrap.require_osx do
|
@@ -15,6 +15,8 @@ module AddressBook
|
|
15
15
|
if granted
|
16
16
|
activate!
|
17
17
|
block.call(self)
|
18
|
+
else
|
19
|
+
block.call(nil)
|
18
20
|
end
|
19
21
|
end
|
20
22
|
else
|
@@ -25,6 +27,10 @@ module AddressBook
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
30
|
+
def self.instance
|
31
|
+
@instance ||= new
|
32
|
+
end
|
33
|
+
|
28
34
|
def activate!
|
29
35
|
@ab = LiveAddrBook.new(AddressBook.address_book)
|
30
36
|
end
|
@@ -41,7 +47,7 @@ module AddressBook
|
|
41
47
|
end
|
42
48
|
|
43
49
|
def auth!
|
44
|
-
raise "iOS Address Book authorization is required." if @ab.nil?
|
50
|
+
raise SecurityError, "iOS Address Book authorization is required." if @ab.nil?
|
45
51
|
end
|
46
52
|
|
47
53
|
def people(opts = {}, &block)
|
@@ -97,6 +103,14 @@ module AddressBook
|
|
97
103
|
"#<#{self.class}:#{"0x%0x" % object_id} #{ab.status}>"
|
98
104
|
end
|
99
105
|
|
106
|
+
def picker(options={}, &after)
|
107
|
+
AddressBook::Picker.show options.merge(ab: self), &after
|
108
|
+
end
|
109
|
+
|
110
|
+
def creator(options, &after)
|
111
|
+
AddressBook::Creator.show(options.merge(ab: self), &after)
|
112
|
+
end
|
113
|
+
|
100
114
|
private
|
101
115
|
|
102
116
|
def ab_people(opts = {})
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module AddressBook
|
2
|
+
class Creator
|
3
|
+
class << self
|
4
|
+
attr_accessor :showing
|
5
|
+
end
|
6
|
+
def self.show(options={}, &after)
|
7
|
+
raise "Cannot show two Pickers" if showing?
|
8
|
+
@creator = self.new(options[:ab], &after)
|
9
|
+
@creator.show options
|
10
|
+
@creator
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.showing?
|
14
|
+
!!showing
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(ab, &after)
|
18
|
+
@ab = ab
|
19
|
+
@after = after
|
20
|
+
end
|
21
|
+
|
22
|
+
def show(options={})
|
23
|
+
self.class.showing = true
|
24
|
+
|
25
|
+
@new_person_ctlr = ABNewPersonViewController.alloc.init
|
26
|
+
@new_person_ctlr.newPersonViewDelegate = self
|
27
|
+
|
28
|
+
@presenter = options.fetch :presenter, UIApplication.sharedApplication.keyWindow.rootViewController
|
29
|
+
@animated = options.fetch :animated, true
|
30
|
+
@presenter.presentViewController(@new_person_ctlr, animated: @animated, completion: nil)
|
31
|
+
end
|
32
|
+
|
33
|
+
def hide(ab_person=nil)
|
34
|
+
person = ab_person && @ab.person(ABRecordGetRecordID(ab_person))
|
35
|
+
|
36
|
+
@presenter.dismissViewControllerAnimated(@animated, completion: lambda do
|
37
|
+
@after.call(person) if @after
|
38
|
+
self.class.showing = false
|
39
|
+
end)
|
40
|
+
end
|
41
|
+
|
42
|
+
def newPersonViewController(new_person_ctlr, didCompleteWithNewPerson: ab_person)
|
43
|
+
hide(ab_person)
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
@@ -60,19 +60,16 @@ module AddressBook
|
|
60
60
|
@mv_type = multi_value_property_type
|
61
61
|
mv = ABMultiValueCreateMutable(mv_type)
|
62
62
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
end
|
72
|
-
else # KABMultiDictionaryPropertyType
|
73
|
-
@attributes.each do |rec|
|
63
|
+
@attributes.each do |rec|
|
64
|
+
label = localized_label(rec[:label])
|
65
|
+
case mv_type
|
66
|
+
when KABMultiStringPropertyType
|
67
|
+
ABMultiValueAddValueAndLabel(mv, rec[:value], label, nil)
|
68
|
+
when KABMultiDateTimePropertyType
|
69
|
+
ABMultiValueAddValueAndLabel(mv, rec[:date], label, nil)
|
70
|
+
else # KABMultiDictionaryPropertyType
|
74
71
|
if value = dict_to_ab_record(rec)
|
75
|
-
ABMultiValueAddValueAndLabel(mv, value,
|
72
|
+
ABMultiValueAddValueAndLabel(mv, value, label, nil)
|
76
73
|
end
|
77
74
|
end
|
78
75
|
end
|
@@ -5,7 +5,7 @@ module AddressBook
|
|
5
5
|
end
|
6
6
|
def self.show(options={}, &after)
|
7
7
|
raise "Cannot show two Pickers" if showing?
|
8
|
-
@picker =
|
8
|
+
@picker = self.new(options[:ab] || AddressBook::AddrBook.instance, &after)
|
9
9
|
@picker.show options
|
10
10
|
@picker
|
11
11
|
end
|
@@ -14,7 +14,8 @@ module AddressBook
|
|
14
14
|
!!showing
|
15
15
|
end
|
16
16
|
|
17
|
-
def initialize(&after)
|
17
|
+
def initialize(ab, &after)
|
18
|
+
@ab = ab
|
18
19
|
@after = after
|
19
20
|
end
|
20
21
|
|
@@ -23,17 +24,19 @@ module AddressBook
|
|
23
24
|
|
24
25
|
@people_picker_ctlr = ABPeoplePickerNavigationController.alloc.init
|
25
26
|
@people_picker_ctlr.peoplePickerDelegate = self
|
26
|
-
|
27
|
-
presenter.
|
27
|
+
|
28
|
+
@presenter = options.fetch :presenter, UIApplication.sharedApplication.keyWindow.rootViewController
|
29
|
+
@animated = options.fetch :animated, true
|
30
|
+
@presenter.presentViewController(@people_picker_ctlr, animated: @animated, completion: nil)
|
28
31
|
end
|
29
32
|
|
30
33
|
def hide(ab_person=nil)
|
31
|
-
person = ab_person
|
34
|
+
person = ab_person && @ab.person(ABRecordGetRecordID(ab_person))
|
32
35
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
36
|
+
@presenter.dismissViewControllerAnimated(@animated, completion: lambda do
|
37
|
+
@after.call(person) if @after
|
38
|
+
self.class.showing = false
|
39
|
+
end)
|
37
40
|
end
|
38
41
|
|
39
42
|
def peoplePickerNavigationController(people_picker, shouldContinueAfterSelectingPerson:ab_person)
|
@@ -49,13 +52,6 @@ module AddressBook
|
|
49
52
|
def peoplePickerNavigationControllerDidCancel(people_picker)
|
50
53
|
hide
|
51
54
|
end
|
52
|
-
end
|
53
|
-
end
|
54
55
|
|
55
|
-
module AddressBook
|
56
|
-
module_function
|
57
|
-
def pick(options={}, &after)
|
58
|
-
AddressBook::Picker.show options, &after
|
59
56
|
end
|
60
57
|
end
|
61
|
-
|
@@ -0,0 +1,33 @@
|
|
1
|
+
describe AddressBook::Creator do
|
2
|
+
before do
|
3
|
+
@ab = AddressBook::AddrBook.new
|
4
|
+
@person = @ab.create_person(first_name: 'Colin')
|
5
|
+
@ab_person = @person.ab_person
|
6
|
+
end
|
7
|
+
|
8
|
+
after do
|
9
|
+
@person.delete!
|
10
|
+
end
|
11
|
+
|
12
|
+
describe 'IOS UI for creating an entry' do
|
13
|
+
before do
|
14
|
+
@created_person = :not_set
|
15
|
+
@picker = @ab.creator(animated: false) do |person|
|
16
|
+
@created_person = person
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should yield the created person' do
|
21
|
+
@picker.newPersonViewController(nil, didCompleteWithNewPerson: @ab_person)
|
22
|
+
@created_person.should.not == nil
|
23
|
+
@created_person.should.be.kind_of?(AddressBook::Person)
|
24
|
+
@created_person.first_name.should == 'Colin'
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should yield nil if canceled' do
|
28
|
+
@created_person.should == :not_set
|
29
|
+
@picker.newPersonViewController(nil, didCompleteWithNewPerson: nil)
|
30
|
+
@created_person.should == nil
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -11,7 +11,7 @@ describe AddressBook::Person do
|
|
11
11
|
describe 'new' do
|
12
12
|
before do
|
13
13
|
@data = new_alex
|
14
|
-
@alex =
|
14
|
+
@alex = @ab.new_person(@data)
|
15
15
|
end
|
16
16
|
it 'should create but not save in the address book' do
|
17
17
|
@alex.should.be.new_record
|
@@ -30,7 +30,7 @@ describe AddressBook::Person do
|
|
30
30
|
describe 'existing' do
|
31
31
|
before do
|
32
32
|
@email = unique_email
|
33
|
-
@alex =
|
33
|
+
@alex = @ab.create_person(new_alex(@email))
|
34
34
|
end
|
35
35
|
after do
|
36
36
|
@alex.delete!
|
@@ -93,16 +93,16 @@ describe AddressBook::Person do
|
|
93
93
|
|
94
94
|
describe '.all' do
|
95
95
|
it 'should have the person we created' do
|
96
|
-
all_names =
|
96
|
+
all_names = @ab.people.map do |person|
|
97
97
|
[person.first_name, person.last_name]
|
98
98
|
end
|
99
99
|
all_names.should.include? [@alex.first_name, @alex.last_name]
|
100
100
|
end
|
101
101
|
|
102
102
|
it 'should get bigger when we create another' do
|
103
|
-
initial_people_count =
|
104
|
-
@person =
|
105
|
-
|
103
|
+
initial_people_count = @ab.count
|
104
|
+
@person = @ab.create_person({:first_name => 'Alex2', :last_name=>'Rothenberg2'})
|
105
|
+
@ab.count.should.equal (initial_people_count + 1)
|
106
106
|
@person.delete!
|
107
107
|
end
|
108
108
|
end
|
@@ -111,7 +111,7 @@ describe AddressBook::Person do
|
|
111
111
|
describe '.find_or_new_by_XXX - new or existing' do
|
112
112
|
before do
|
113
113
|
@email = unique_email
|
114
|
-
@alex =
|
114
|
+
@alex = @ab.create_person(new_alex(@email))
|
115
115
|
end
|
116
116
|
after do
|
117
117
|
@alex.delete!
|
@@ -175,7 +175,7 @@ describe AddressBook::Person do
|
|
175
175
|
|
176
176
|
describe 'a new person' do
|
177
177
|
before do
|
178
|
-
@ab_person =
|
178
|
+
@ab_person = @ab.new_person(@attributes)
|
179
179
|
end
|
180
180
|
|
181
181
|
it 'should not be existing' do
|
@@ -314,7 +314,7 @@ describe AddressBook::Person do
|
|
314
314
|
|
315
315
|
describe 'an existing person' do
|
316
316
|
before do
|
317
|
-
@orig_ab_person =
|
317
|
+
@orig_ab_person = @ab.new_person(@attributes)
|
318
318
|
@orig_ab_person.save
|
319
319
|
@ab_person = AddressBook::Person.find_or_new_by_email(@attributes[:emails][0][:value])
|
320
320
|
end
|
@@ -356,7 +356,7 @@ describe AddressBook::Person do
|
|
356
356
|
{ :label => 'work' },
|
357
357
|
{ :label => 'work', :url => 'http://state.edu/college' }
|
358
358
|
]
|
359
|
-
@ab_person =
|
359
|
+
@ab_person = @ab.create_person(@attributes)
|
360
360
|
end
|
361
361
|
after do
|
362
362
|
@ab_person.delete!
|
@@ -381,7 +381,7 @@ describe AddressBook::Person do
|
|
381
381
|
|
382
382
|
describe "organization record" do
|
383
383
|
before do
|
384
|
-
@person =
|
384
|
+
@person = @ab.new_person(
|
385
385
|
:first_name => 'John',
|
386
386
|
:last_name => 'Whorfin',
|
387
387
|
:organization => 'Acme Inc.',
|
@@ -397,7 +397,7 @@ describe AddressBook::Person do
|
|
397
397
|
|
398
398
|
describe 'method missing magic' do
|
399
399
|
before do
|
400
|
-
@person =
|
400
|
+
@person = @ab.new_person({})
|
401
401
|
end
|
402
402
|
describe 'getters' do
|
403
403
|
it 'should have a getter for each attribute' do
|
@@ -466,36 +466,64 @@ describe AddressBook::Person do
|
|
466
466
|
|
467
467
|
describe "sorting" do
|
468
468
|
before do
|
469
|
+
# warn "BEFORE CREATION COUNT: #{@ab.count}"
|
469
470
|
@ab.people.each(&:delete!)
|
470
471
|
|
471
|
-
@p1 = @ab.create_person(
|
472
|
-
|
473
|
-
@
|
474
|
-
@
|
475
|
-
@
|
472
|
+
@p1 = @ab.create_person(first_name: 'Bob', last_name: 'Edwards')
|
473
|
+
# warn "I CREATED #{@p1}"
|
474
|
+
@p2 = @ab.create_person(first_name: 'Doris', last_name: 'Channing')
|
475
|
+
@p3 = @ab.create_person(first_name: 'Anne', last_name: 'Brown')
|
476
|
+
@p4 = @ab.create_person(first_name: 'Eddie', last_name: 'Anderson')
|
477
|
+
@p5 = @ab.create_person(first_name: 'Carol', last_name: 'Dolittle')
|
478
|
+
# warn "AFTER CREATION COUNT: #{@ab.count}"
|
476
479
|
end
|
477
480
|
|
478
|
-
|
479
|
-
|
480
|
-
|
481
|
-
|
482
|
-
|
481
|
+
after do
|
482
|
+
# warn "BEFORE CLEANUP COUNT: #{@ab.count}"
|
483
|
+
@ab.people.each(&:delete!)
|
484
|
+
# warn "AFTER CLEANUP COUNT: #{@ab.count}"
|
485
|
+
# [@p1, @p2, @p3, @p4, @p5].each(&:delete!)
|
483
486
|
end
|
484
487
|
|
485
|
-
|
486
|
-
|
487
|
-
|
488
|
-
|
489
|
-
|
490
|
-
|
488
|
+
describe "with different sort orders" do
|
489
|
+
it "should sort on last name using OS sort" do
|
490
|
+
# warn "PEOPLE COUNT IS #{@ab.people.count}"
|
491
|
+
expectation = [@p4, @p3, @p2, @p5, @p1].map(&:uid)
|
492
|
+
@ab.people(ordering: KABPersonSortByLastName).map(&:uid).should.equal expectation
|
493
|
+
end
|
494
|
+
# it "should support last-name sort in Person#all" do
|
495
|
+
# AddressBook::Person.all(ordering: KABPersonSortByLastName).map(&:uid).should.equal [@p4, @p3, @p2, @p5, @p1]
|
496
|
+
# end
|
497
|
+
|
498
|
+
it "should pass" do
|
499
|
+
3.should == 3
|
500
|
+
end
|
501
|
+
|
502
|
+
it "should sort on first name using OS sort" do
|
503
|
+
# warn "PEOPLE COUNT IS #{@ab.people.count}"
|
504
|
+
expectation = [@p3, @p1, @p5, @p2, @p4].map(&:uid)
|
505
|
+
@ab.people(ordering: KABPersonSortByFirstName).map(&:uid).should.equal expectation
|
506
|
+
end
|
507
|
+
# it "should support first-name sort in Person#all" do
|
508
|
+
# AddressBook::Person.all(ordering: KABPersonSortByFirstName).map(&:uid).should.equal [@p3, @p1, @p5, @p2, @p4]
|
509
|
+
# end
|
491
510
|
|
492
|
-
|
493
|
-
|
494
|
-
|
511
|
+
it "should pass" do
|
512
|
+
3.should == 3
|
513
|
+
end
|
514
|
+
|
515
|
+
it "should support a custom sort order" do
|
516
|
+
# warn "@ab.people is #{@ab.people}"
|
517
|
+
# warn "PEOPLE COUNT IS #{@ab.people.count}"
|
518
|
+
ordered = @ab.people { |p| p.last_name[1] }.map(&:uid)
|
519
|
+
# warn "sorted is #{ordered}"
|
520
|
+
expectation = [@p1, @p2, @p4, @p5, @p3].map(&:uid)
|
521
|
+
ordered.should.equal expectation
|
522
|
+
end
|
495
523
|
end
|
496
524
|
end
|
497
525
|
|
498
|
-
describe "notifications" do
|
526
|
+
describe "notifications across AB instances" do
|
499
527
|
before do
|
500
528
|
@ab1 = AddressBook::AddrBook.new
|
501
529
|
@ab2 = AddressBook::AddrBook.new
|
@@ -505,15 +533,26 @@ describe AddressBook::Person do
|
|
505
533
|
App.notification_center.observe :addressbook_updated do |notification|
|
506
534
|
@notifications += 1
|
507
535
|
end
|
536
|
+
end
|
508
537
|
|
538
|
+
# should see a single notification for each change to the AB database:
|
539
|
+
# 2 creations, 2 deletions
|
540
|
+
it "should come in once for every external change" do
|
509
541
|
@alice = @ab2.create_person({first_name: 'Alice'})
|
510
542
|
@bob = @ab1.create_person({first_name: 'Bob'})
|
511
543
|
@alice.delete!
|
512
544
|
@bob.delete!
|
513
|
-
end
|
514
545
|
|
515
|
-
it "should be notified of every change" do
|
516
546
|
@notifications.should.equal 4
|
517
547
|
end
|
518
548
|
end
|
549
|
+
|
550
|
+
after do
|
551
|
+
puts
|
552
|
+
NSLog("=============== person_spec.rb line #{__LINE__} ===============")
|
553
|
+
wait 1 do
|
554
|
+
NSLog("=============== person_spec.rb line #{__LINE__} ===============")
|
555
|
+
end
|
556
|
+
end
|
557
|
+
|
519
558
|
end
|
@@ -1,34 +1,36 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
describe AddressBook::Picker do
|
2
|
+
describe 'IOS UI for finding people' do
|
3
|
+
before do
|
4
|
+
@ab = AddressBook::AddrBook.new
|
5
|
+
@colin = @ab.create_person(first_name: 'Colin')
|
6
|
+
@ab_person = @colin.ab_person
|
7
|
+
@selected_person = nil
|
8
|
+
@picker = @ab.picker(animated: false) do |person|
|
9
|
+
@selected_person = person
|
10
|
+
end
|
11
|
+
end
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
# @selected_person.should.not == nil
|
16
|
-
# @selected_person.first_name.should == 'Colin'
|
17
|
-
# end
|
13
|
+
after do
|
14
|
+
@colin.delete!
|
15
|
+
end
|
18
16
|
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# @selected_person.should.not == nil
|
25
|
-
# @selected_person.first_name.should == 'Colin'
|
26
|
-
# end
|
17
|
+
it 'should yield the selected person' do
|
18
|
+
@picker.peoplePickerNavigationController(@picker_nav_controller, shouldContinueAfterSelectingPerson: @ab_person)
|
19
|
+
@selected_person.should.not == nil
|
20
|
+
@selected_person.first_name.should == 'Colin'
|
21
|
+
end
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
23
|
+
it 'should yield the selected person' do
|
24
|
+
property = :some_property
|
25
|
+
id = :some_id
|
26
|
+
@picker.peoplePickerNavigationController(@picker_nav_controller, shouldContinueAfterSelectingPerson: @ab_person, property:property, identifier:id)
|
27
|
+
@selected_person.should.not == nil
|
28
|
+
@selected_person.first_name.should == 'Colin'
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'should yield nil when cancelled' do
|
32
|
+
@picker.peoplePickerNavigationControllerDidCancel(@picker_nav_controller)
|
33
|
+
@selected_person.should == nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,48 +1,27 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
`rm -rf \"#{AB_PATH_BAK}\"`
|
27
|
-
`mv \"#{AB_PATH}\" \"#{AB_PATH_BAK}\"`
|
28
|
-
# Kernel.system "rm -rf \"#{AB_PATH_BAK}\""
|
29
|
-
# Kernel.system "mv \"#{AB_PATH}\" \"#{AB_PATH_BAK}\""
|
30
|
-
end
|
31
|
-
|
32
|
-
at_exit do
|
33
|
-
warn "RESTORING ORIGINAL ADDRESS BOOK IN SIMULATOR"
|
34
|
-
|
35
|
-
Kernel.system "rm -rf \"#{AB_PATH}\""
|
36
|
-
Kernel.system "mv \"#{AB_PATH_BAK}\" \"#{AB_PATH}\""
|
37
|
-
end
|
38
|
-
|
39
|
-
def wait_for_authorization
|
40
|
-
@semaphore = Dispatch::Semaphore.new(0)
|
41
|
-
AddressBook::AddrBook.new do
|
42
|
-
@semaphore.signal
|
1
|
+
module Bacon
|
2
|
+
class << self
|
3
|
+
@@old_run = instance_method(:run)
|
4
|
+
@@already_started = false
|
5
|
+
|
6
|
+
def ab_connect
|
7
|
+
AddressBook::AddrBook.new do |ab|
|
8
|
+
if ab
|
9
|
+
EM.schedule_on_main do
|
10
|
+
Bacon.run
|
11
|
+
end
|
12
|
+
else
|
13
|
+
warn "ACCESS DENIED - ABORTING"
|
14
|
+
exit
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
if AddressBook.authorized?
|
21
|
+
@@old_run.bind(self).call
|
22
|
+
else
|
23
|
+
ab_connect
|
24
|
+
end
|
25
|
+
end
|
43
26
|
end
|
44
|
-
@semaphore.wait
|
45
27
|
end
|
46
|
-
|
47
|
-
protect_existing_address_book
|
48
|
-
wait_for_authorization
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion-addressbook
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Alex Rothenberg
|
@@ -9,48 +9,48 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2014-04-14 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: bubble-wrap
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
|
-
- - ~>
|
18
|
+
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
20
|
version: '1.3'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
|
-
- - ~>
|
25
|
+
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
27
|
version: '1.3'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: rake
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
31
31
|
requirements:
|
32
|
-
- -
|
32
|
+
- - ">="
|
33
33
|
- !ruby/object:Gem::Version
|
34
34
|
version: '0'
|
35
35
|
type: :development
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
|
-
- -
|
39
|
+
- - ">="
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '0'
|
42
42
|
- !ruby/object:Gem::Dependency
|
43
43
|
name: rspec
|
44
44
|
requirement: !ruby/object:Gem::Requirement
|
45
45
|
requirements:
|
46
|
-
- -
|
46
|
+
- - ">="
|
47
47
|
- !ruby/object:Gem::Version
|
48
48
|
version: '0'
|
49
49
|
type: :development
|
50
50
|
prerelease: false
|
51
51
|
version_requirements: !ruby/object:Gem::Requirement
|
52
52
|
requirements:
|
53
|
-
- -
|
53
|
+
- - ">="
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '0'
|
56
56
|
description: A RubyMotion wrapper around the iOS & OSX Address Book frameworks
|
@@ -61,19 +61,20 @@ executables: []
|
|
61
61
|
extensions: []
|
62
62
|
extra_rdoc_files: []
|
63
63
|
files:
|
64
|
-
- .gitignore
|
65
|
-
- .travis.yml
|
64
|
+
- ".gitignore"
|
65
|
+
- ".travis.yml"
|
66
66
|
- Gemfile
|
67
67
|
- LICENSE
|
68
68
|
- README.md
|
69
69
|
- Rakefile
|
70
|
+
- app/app_delegate.rb
|
70
71
|
- lib/motion-addressbook.rb
|
71
72
|
- lib/motion-addressbook/version.rb
|
72
73
|
- motion-addressbook.gemspec
|
73
74
|
- motion/address_book.rb
|
74
75
|
- motion/address_book/ios/addr_book.rb
|
76
|
+
- motion/address_book/ios/creator.rb
|
75
77
|
- motion/address_book/ios/group.rb
|
76
|
-
- motion/address_book/ios/multi_value.rb
|
77
78
|
- motion/address_book/ios/multi_valued.rb
|
78
79
|
- motion/address_book/ios/person.rb
|
79
80
|
- motion/address_book/ios/picker.rb
|
@@ -82,6 +83,7 @@ files:
|
|
82
83
|
- motion/address_book/osx/group.rb
|
83
84
|
- motion/address_book/osx/multi_valued.rb
|
84
85
|
- motion/address_book/osx/person.rb
|
86
|
+
- spec/ios/address_book/creator_spec.rb
|
85
87
|
- spec/ios/address_book/group_spec.rb
|
86
88
|
- spec/ios/address_book/multi_valued_spec.rb
|
87
89
|
- spec/ios/address_book/person_spec.rb
|
@@ -104,21 +106,22 @@ require_paths:
|
|
104
106
|
- lib
|
105
107
|
required_ruby_version: !ruby/object:Gem::Requirement
|
106
108
|
requirements:
|
107
|
-
- -
|
109
|
+
- - ">="
|
108
110
|
- !ruby/object:Gem::Version
|
109
111
|
version: '0'
|
110
112
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
113
|
requirements:
|
112
|
-
- -
|
114
|
+
- - ">="
|
113
115
|
- !ruby/object:Gem::Version
|
114
116
|
version: '0'
|
115
117
|
requirements: []
|
116
118
|
rubyforge_project:
|
117
|
-
rubygems_version: 2.
|
119
|
+
rubygems_version: 2.2.2
|
118
120
|
signing_key:
|
119
121
|
specification_version: 4
|
120
122
|
summary: A RubyMotion wrapper around the iOS & OSX Address Book frameworks
|
121
123
|
test_files:
|
124
|
+
- spec/ios/address_book/creator_spec.rb
|
122
125
|
- spec/ios/address_book/group_spec.rb
|
123
126
|
- spec/ios/address_book/multi_valued_spec.rb
|
124
127
|
- spec/ios/address_book/person_spec.rb
|
@@ -1,120 +0,0 @@
|
|
1
|
-
module AddressBook
|
2
|
-
class MultiValue
|
3
|
-
attr_reader :attributes, :ab_multi_values
|
4
|
-
|
5
|
-
def self.attribute_map
|
6
|
-
{ :mobile => KABPersonPhoneMobileLabel ,
|
7
|
-
:iphone => KABPersonPhoneIPhoneLabel ,
|
8
|
-
:main => KABPersonPhoneMainLabel ,
|
9
|
-
:home_fax => KABPersonPhoneHomeFAXLabel,
|
10
|
-
:work_fax => KABPersonPhoneWorkFAXLabel,
|
11
|
-
:pager => KABPersonPhonePagerLabel ,
|
12
|
-
:work => KABWorkLabel ,
|
13
|
-
:home => KABHomeLabel ,
|
14
|
-
:other => KABOtherLabel
|
15
|
-
}
|
16
|
-
end
|
17
|
-
def attribute_map
|
18
|
-
self.class.attribute_map
|
19
|
-
end
|
20
|
-
|
21
|
-
def alex
|
22
|
-
ABMultiValueGetIdentifierAtIndex @ab_multi_values, 0
|
23
|
-
end
|
24
|
-
|
25
|
-
def initialize(attributes={}, existing_ab_multi_values=nil)
|
26
|
-
@attributes = {}
|
27
|
-
if existing_ab_multi_values
|
28
|
-
@ab_multi_values = ABMultiValueCreateMutableCopy(existing_ab_multi_values)
|
29
|
-
load_attributes_from_ab
|
30
|
-
else
|
31
|
-
@ab_multi_values = ABMultiValueCreateMutable(KABMultiStringPropertyType)
|
32
|
-
end
|
33
|
-
attributes.each do |attribute, value|
|
34
|
-
send("#{attribute}=", value)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
def method_missing(name, *args)
|
39
|
-
if attribute_name = getter?(name)
|
40
|
-
get(attribute_name)
|
41
|
-
elsif attribute_name = setter?(name)
|
42
|
-
set(attribute_name, args.first)
|
43
|
-
else
|
44
|
-
super
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.is_attribute?(attribute_name)
|
49
|
-
return false if attribute_name.nil?
|
50
|
-
attribute_map.include?(attribute_name.to_sym) || [:email, :phone_number].include?( attribute_name.to_sym)
|
51
|
-
end
|
52
|
-
|
53
|
-
def getter?(method_name)
|
54
|
-
if self.class.is_attribute? method_name
|
55
|
-
method_name
|
56
|
-
else
|
57
|
-
nil
|
58
|
-
end
|
59
|
-
end
|
60
|
-
def setter?(method_name)
|
61
|
-
method_name.to_s =~ /^(\w*)=$/
|
62
|
-
if self.class.is_attribute? $1
|
63
|
-
$1
|
64
|
-
else
|
65
|
-
nil
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
def get(attribute_name)
|
70
|
-
attributes[attribute_name.to_sym] ||= get_field(attribute_map[attribute_name])
|
71
|
-
end
|
72
|
-
|
73
|
-
def set(attribute_name, value)
|
74
|
-
set_field(attribute_map[attribute_name.to_sym], value)
|
75
|
-
attributes[attribute_name.to_sym] = value
|
76
|
-
end
|
77
|
-
|
78
|
-
def set_field(ab_label, value)
|
79
|
-
if blank?(ab_label)
|
80
|
-
ABMultiValueAddValueAndLabel(@ab_multi_values, value, ab_label, nil) unless value.nil?
|
81
|
-
else
|
82
|
-
ABMultiValueReplaceValueAtIndex(@ab_multi_values, value, 0)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
def get_field(ab_label)
|
87
|
-
puts
|
88
|
-
puts [__FILE__, __LINE__, ab_label].inspect
|
89
|
-
index = ABMultiValueGetIndexForIdentifier(@ab_multi_values, ab_label)
|
90
|
-
puts [__FILE__, __LINE__, index].inspect
|
91
|
-
ABMultiValueCopyValueAtIndex(@ab_multi_values, index)
|
92
|
-
end
|
93
|
-
|
94
|
-
def values
|
95
|
-
attributes.values
|
96
|
-
end
|
97
|
-
|
98
|
-
def include? value
|
99
|
-
return false if values.nil?
|
100
|
-
values.include? value
|
101
|
-
end
|
102
|
-
|
103
|
-
def size
|
104
|
-
ABMultiValueGetCount(@ab_multi_values)
|
105
|
-
end
|
106
|
-
|
107
|
-
def load_attributes_from_ab
|
108
|
-
(0...size).to_a.each do |i|
|
109
|
-
label = ABMultiValueCopyLabelAtIndex(@ab_multi_values, i)
|
110
|
-
@attributes[attribute_map.invert[label]] = ABMultiValueCopyValueAtIndex(@ab_multi_values, i)
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
private
|
115
|
-
def blank?(ab_label)
|
116
|
-
attributes[ab_label].nil?
|
117
|
-
end
|
118
|
-
|
119
|
-
end
|
120
|
-
end
|