require 'osx/cocoa' OSX.require_framework "AddressBook" OSX.ns_import :ABAddressBook OSX.ns_import :ABPerson OSX.ns_import :ABMultiValue class OSX::ABPerson def first_name; valueForProperty(OSX::KABFirstNameProperty).to_s; end def last_name; valueForProperty(OSX::KABLastNameProperty).to_s; end def company; valueForProperty(OSX::KABOrganizationProperty).to_s; end def email_addresses valueForProperty(OSX::KABEmailProperty).to_a.map {|address| address.to_s } end def flags valueForProperty(OSX::KABPersonFlags).to_i end def company? (flags & OSX::KABShowAsMask) == OSX::KABShowAsCompany end def name if company? && !company.empty? company else "#{first_name} #{last_name}" end end end class OSX::ABAddressBook # returns an array of ABPerson objects that match the search term def match(term, fields = [:FirstName, :LastName, :Organization, :Email]) return [] if term.nil? || term.empty? matchers = fields.map do |field| label = OSX.const_get('KAB' + field.to_s + 'Property') OSX::ABPerson.searchElementForProperty_label_key_value_comparison( label, nil, nil, term, OSX::KABContainsSubStringCaseInsensitive ) end recordsMatchingSearchElement( OSX::ABSearchElement.searchElementForConjunction_children( OSX::KABSearchOr, matchers ) ).to_a end end class OSX::ABMultiValue def to_a (0...count).map {|i| valueAtIndex(i) } end end OSX::ABAddressBook.sharedAddressBook.people.to_a.map do |person| person.email_addresses.map {|address| "#{person.name} <#{address}>" } end.flatten.sort