motion-addressbook 1.5.0 → 1.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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
@@ -8,8 +8,9 @@ Gemfile.lock
8
8
  vendor/Pods/Pods.xcodeproj/project.pbxproj
9
9
  vendor/Pods/build*
10
10
 
11
- abhack/*.bridgesupport
12
- abhack/build*
13
-
14
11
  .dat*.00*
15
12
  pkg
13
+ .projectile
14
+ doc
15
+
16
+ TAGS
data/README.md CHANGED
@@ -1,6 +1,10 @@
1
- # Addressbook for RubyMotion[![Build Status](https://secure.travis-ci.org/alexrothenberg/motion-addressbook.png)](http://travis-ci.org/alexrothenberg/motion-addressbook) [![Code Climate](https://codeclimate.com/github/alexrothenberg/motion-addressbook.png)](https://codeclimate.com/github/alexrothenberg/motion-addressbook) [![Gem Version](https://badge.fury.io/rb/motion-addressbook.png)](http://badge.fury.io/rb/motion-addressbook)
1
+ # Addressbook for RubyMotion
2
2
 
3
- A RubyMotion wrapper around the iOS Address Book framework for RubyMotion apps.
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 asking the user for permission before it allows an app to access the AddressBook. There are 3 ways to interact with this
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
- people = AddressBook::Person.all
42
- # A dialog may be presented to the user before "people" was returned
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
- ### Instantiating a person object
101
+ You can also specify the presenting controller:
91
102
 
92
- There are 3 ways to instantiate a person object
103
+ ```ruby
104
+ AddressBook.pick presenter: self do |person|
105
+ ...
106
+ end
107
+ ```
108
+
109
+ ### Working with Person objects
93
110
 
94
- ### To get a new person not yet connected to the iOS Address Book
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::Person.new
98
- # => #<AddressBook::Person:0x8c67ca0 @attributes={:first_name=>nil, :last_name=>nil, :job_title=>nil, :department=>nil, :organization=>nil} @new_record=true @ab_person=#<__NSCFType:0x6d832e0>>
116
+ ab = AddressBook::AddrBook.new
117
+ ab.people
118
+ => [#<AddressBook::Person:3: {:first_name=>"John", :last_name=>"Appleseed", ...}>, ...]
99
119
  ```
100
120
 
101
- ### To get a list of existing people from the iOS Address Book
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
- Get all people with `.all`
132
+ Construct a new blank Person but do not store it immediately in the Address Book.
104
133
 
105
134
  ```ruby
106
- AddressBook::Person.all
107
- # => [#<AddressBook::Person:0x6d55e90 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0x6df8bf0>>,
108
- # #<AddressBook::Person:0x6d550a0 @attributes={:first_name=>"Laurent", :last_name=>"Sansonetti", :job_title=>nil, :department=>nil, :organization=>"HipByte"} @ab_person=#<__NSCFType:0x6df97d0>>]
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
- # => [#<AddressBook::Person:0x6d55e90 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0x6df8bf0>>]
146
+ => [#<AddressBook::Person:14: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
116
147
  ```
117
148
 
118
- Get the first person matching one attribute with `find_by_XXX`
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
- # => #<AddressBook::Person:0x6d55e90 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0x6df8bf0>>
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
- # => [#<AddressBook::Person:0x6d55e90 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0x6df8bf0>>]
160
+ => [#<AddressBook::Person:14: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
130
161
  ```
131
162
 
132
- To look for an existing person or get a new one if none is found `find_or_new_by_XXX`
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
- # => #<AddressBook::Person:0xe4e3a80 @attributes={:first_name=>"Alex", :last_name=>"Rothenberg", :job_title=>nil, :department=>nil, :organization=>nil} @ab_person=#<__NSCFType:0xe4bbef0>>
167
+ => #<AddressBook::Person:17: {:first_name=>"Alex", :last_name=>"Rothenberg", ...}>]
137
168
  ```
138
169
 
139
- ### Create a new Contact and save in Contacts app
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
@@ -1,5 +1,5 @@
1
1
  module Motion
2
2
  module Addressbook
3
- VERSION = "1.5.0"
3
+ VERSION = "1.6.2"
4
4
  end
5
5
  end
@@ -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 = AddressBook.address_book
7
- end
8
- def people(opts = {})
9
- if opts[:source]
10
- ABAddressBookCopyArrayOfAllPeopleInSource(ab, opts[:source].ab_source).map do |ab_person|
11
- AddressBook::Person.new({}, ab_person, :address_book => ab)
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
- ABAddressBookCopyArrayOfAllPeople(ab).map do |ab_person|
15
- AddressBook::Person.new({}, ab_person, :address_book => ab)
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
- ABAddressBookGetPersonCount(@ab)
48
+ ab.person_count
21
49
  end
22
50
  def new_person(attributes)
23
- Person.new(attributes, nil, :address_book => @ab)
51
+ Person.new(attributes, nil, :address_book => ab.ab)
24
52
  end
25
53
  def create_person(attributes)
26
- p = Person.new(attributes, nil, :address_book => @ab)
54
+ p = new_person(attributes)
27
55
  p.save
28
56
  p
29
57
  end
30
58
  def person(id)
31
- (p = ABAddressBookGetPersonWithRecordID(ab, id)) && Person.new(nil, p, :address_book => ab)
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
- ABAddressBookCopyArrayOfAllGroups(@ab).map do |ab_group|
39
- AddressBook::Group.new(:ab_group => ab_group, :address_book => @ab)
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 => @ab)
74
+ AddressBook::Group.new(:attributes => attributes, :address_book => ab.ab)
44
75
  end
45
76
  def group(id)
46
- (g = ABAddressBookGetGroupWithRecordID(ab, id)) && Group.new(:ab_group => g, :address_book => @ab)
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
- ABAddressBookRegisterExternalChangeCallback(ab, callback, context)
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
- # ABAddressBookCopyArrayOfAllSources(ab).map {|s| ABRecordCopyValue(s, KABSourceTypeProperty)}
55
- ABAddressBookCopyArrayOfAllSources(ab).map {|s| Source.new(s)}
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[:address_book]
12
- if opts[:ab_group]
13
- # import existing
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 = nil
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={}, existing_ab_person = nil, opts = {})
6
- @address_book = opts[:address_book]
7
- if existing_ab_person.nil?
8
- @ab_person = nil
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
- ab = AddressBook.address_book
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
- uid
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.attributes.map {|rec| rec[:value]}.any? {|v| v == conditions[attribute]}
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.attributes.map {|r| r[:value]}
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.attributes.map {|r| r[:value]}
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.attributes.first[:value]; end
264
- def address; addresses.attributes.first; end
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
- # workaround for RubyMotion bug: blows up when fetching NSDate properties
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
- ABHack.getDateProperty(KABPersonCreationDateProperty, from: ab_person)
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
- if value = get_multi_valued(ab_property)
431
- if value.attributes.any?
432
- @attributes[attr_key] = value.attributes
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
- if field == KABPersonBirthdayProperty
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.presentViewController(@people_picker_ctlr, animated:true, completion:nil)
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
 
@@ -9,9 +9,9 @@ module AddressBook
9
9
  def type
10
10
  ABRecordCopyValue(ab_source, KABSourceTypeProperty)
11
11
  end
12
- end
13
12
 
14
- def local?
15
- type == KABSourceTypeLocal
13
+ def local?
14
+ type == KABSourceTypeLocal
15
+ end
16
16
  end
17
17
  end
@@ -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
- ABAddressBookRegisterExternalChangeCallback(ab, callback, context)
62
+ App.notification_center.observe KABDatabaseChangedExternallyNotification do |notification|
63
+ callback.call(context)
64
+ end
60
65
  end
61
66
 
62
- # def sources
63
- # # ABAddressBookCopyArrayOfAllSources(ab).map {|s| ABRecordCopyValue(s, KABSourceTypeProperty)}
64
- # ABAddressBookCopyArrayOfAllSources(ab).map {|s| Source.new(s)}
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[:address_book]
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, error)
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[:address_book]
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(name, *args)
45
- if property = ReverseSingleValuePropertyMap[name]
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
- KABPrefixProperty => :prefix,
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.attributes.any?
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
@@ -5,10 +5,10 @@ module AddressBook
5
5
  if App.osx?
6
6
  ABAddressBook.addressBook
7
7
  else # iOS
8
- if Device.ios_version == '6.0'
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
- @address_book = ABAddressBookCreateWithOptions(nil, error)
27
- request_authorization unless authorized?
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.new_person({:first_name => 'Alice', :emails => [{:label => 'home', :value => 'alice@example.com'}]})
36
- @p1.save
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 << @p1
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 @alex_data[:middle_name ]
47
- @the_person.suffix.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 ]
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 @alex_data[:note]
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.0
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-06-12 00:00:00.000000000 Z
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: 1.8.24
116
+ rubygems_version: 2.0.5
132
117
  signing_key:
133
- specification_version: 3
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
@@ -1,8 +0,0 @@
1
- #import <AddressBook/AddressBook.h>
2
-
3
- @interface ABHack : NSObject
4
-
5
- + (NSDate *) getDateProperty: (ABPropertyID) property from: (ABRecordRef) ab_person;
6
- + (NSDate *) getDateValueAtIndex: (int) index from: (ABMutableMultiValueRef) ab_multi_value;
7
-
8
- @end
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