alexrothenberg/motion-addressbook

View on GitHub
motion/address_book/osx/group.rb

Summary

Maintainability
A
2 hrs
Test Coverage
# Wrapper for OSX ABGroup
#
# * groups are saved to the database immediately upon new()
# * members are added with <<
#
module AddressBook
  class Group
    attr_reader :attributes, :error
    attr_reader :address_book

    def initialize(opts)
      @address_book = opts.fetch(:address_book) { AddressBook.address_book }
      if opts[:ab_group]
        # import existing
        @ab_group = opts[:ab_group]
        @attributes = nil
      else
        # create new
        @ab_group = nil
        @attributes = opts[:attributes]
      end
    end

    def save
      address_book.addRecord(ab_group)
      address_book.save
      @attributes = nil
      self
    end

    def exists?
      address_book.recordForUniqueId(uid)
    end
    def new_record?
      !exists?
    end
    alias :new? :new_record?

    def delete!
      unless new?
        address_book.removeRecord(ab_group)
        address_book.save
        @ab_group = nil
        self
      end
    end

    def ab_group
      @ab_group || convert_dict_to_ab
    end
    alias :ab_record :ab_group

    def get_field(field)
      ab_group.valueForProperty(field)
    end

    def uid
      get_field(KABUIDProperty)
    end

    def name
      get_field(KABGroupNameProperty)
    end
    def name=(newname)
      ab_group.setValue(newname, forProperty: KABGroupNameProperty)
    end

    def size
      members.count
    end

    def members
      people + subgroups
    end
    def people
      ab_group.members.map do |ab_person|
        AddressBook::Person.new(ab_person, :address_book => address_book)
      end
    end
    def subgroups
      ab_group.subgroups.map do |subgroup|
        AddressBook::Group.new(:ab_group => subgroup, :address_book => address_book)
      end
    end

    def <<(person_or_group)
      raise ArgumentError, "Must save member before adding to group" if person_or_group.new?
      ABGroupAddMember(ab_group, person_or_group.ab_record)
    end
    def add(*items)
      items.each { |item| self << item }
      self
    end
    def remove(person_or_group)
      ABGroupRemoveMember(ab_group, person_or_group.ab_record)
      self
    end

    def local?
      !ab_group.isReadOnly
    end

    def apple_uuid
      get_field('com.apple.uuid')
    end
    # regular groups have a value in the internal "com.apple.uuid" property
    # groups with nil here appear to include
    # * all-local-contacts source (group name is "card")
    # * Facebook source (group name is "addressbook")
    # I suspect that Exchange and DAV sources will show up here too.
    def special?
      apple_uuid.nil?
    end

    private

    def convert_dict_to_ab
      @ab_group = ABGroup.alloc.initWithAddressBook(address_book)

      # groups only have a single regular attribute (name)
      if v = @attributes[:name]
        ab_group.setValue(v, forProperty:KABGroupNameProperty)
      end

      save

      @ab_group
    end
  end
end