motion-addressbook 1.7.0 → 1.7.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 +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
|