class Vpim::Vcard::Maker

A class to make and make changes to vCards.

It can be used to create completely new vCards using Vcard#make2.

Its is also yielded from Vpim::Vcard#make, in which case it allows a kind of transactional approach to changing vCards, so their values can be validated after any changes have been made.

Examples:

Public Class Methods

make(full_name = nil) { |maker| ... } click to toggle source

Deprecated, use make2.

If set, the FN field will be set to full_name. Otherwise, FN will be set from the values in name.

# File lib/vpim/vcard.rb, line 1065
def self.make(full_name = nil, &block) # :yields: maker
  new(full_name, Vpim::Vcard.create).make(&block)
end
make2(card = Vpim::Vcard.create) { |maker| ... } click to toggle source

Make a vCard.

Yields maker, a Vpim::Vcard::Maker which allows fields to be added to card, and returns card, a Vpim::Vcard.

If card is nil or not provided a new Vpim::Vcard is created and the fields are added to it.

Defaults:

  • vCards must have both an N and an FN field, make2 will fail if there is no N field in the card when your block is finished adding fields.

  • If there is an N field, but no FN field, FN will be set from the information in N, see Vcard::Name#preformatted for more information.

  • vCards must have a VERSION field. If one does not exist when your block is is finished it will be set to 3.0.

# File lib/vpim/vcard.rb, line 1057
def self.make2(card = Vpim::Vcard.create, &block) # :yields: maker
  new(nil, card).make(&block)
end

Public Instance Methods

add_addr() { |address| ... } click to toggle source

Add an address field, ADR. address is a Vpim::Vcard::Address.

# File lib/vpim/vcard.rb, line 1155
def add_addr # :yield: address
  x = Vpim::Vcard::Address.new
  yield x
  @card << x.encode
  self
end
add_email(email) { |email| ... } click to toggle source

Add an email field, EMAIL. email is a Vpim::Vcard::Email.

The block is optional, its only necessary if you want to specify the optional attributes.

# File lib/vpim/vcard.rb, line 1179
def add_email(email) # :yield: email
  x = Vpim::Vcard::Email.new(email)
  if block_given?
    yield x
  end
  @card << x.encode
  self
end
add_field(field) click to toggle source

Add a Field, field.

# File lib/vpim/vcard.rb, line 1368
def add_field(field)
  fieldname = field.name.upcase
  case
  when [ 'BEGIN', 'END' ].include?(fieldname)
    raise Vpim::InvalidEncodingError, "Not allowed to manually add #{field.name} to a vCard."

  when [ 'VERSION', 'N', 'FN' ].include?(fieldname)
    if @card.field(fieldname)
      raise Vpim::InvalidEncodingError, "Not allowed to add more than one #{fieldname} to a vCard."
    end
    @card << field

  else
    @card << field
  end
end
add_impp(url) { |impp| ... } click to toggle source

Add an instant-messaging/point of presence address field, IMPP. The address is a URL, with the syntax depending on the protocol.

Attributes of IMPP are:

  • preferred: true - set if this is the preferred address

  • location: home, work, mobile - location of address

  • purpose: personal,business - purpose of communications

All attributes are optional, and so is the block.

The URL syntaxes for the messaging schemes is fairly complicated, so I don't try and build the URLs here, maybe in the future. This forces the user to know the URL for their own address, hopefully not too much of a burden.

IMPP is defined in draft-jennings-impp-vcard-04.txt. It refers to the URI scheme of a number of messaging protocols, but doesn't give references to all of them:

  • “xmpp” indicates to use XMPP, draft-saintandre-xmpp-uri-06.txt

  • “irc” or “ircs” indicates to use IRC, draft-butcher-irc-url-04.txt

  • “sip” indicates to use SIP/SIMPLE, RFC 3261

  • “im” or “pres” indicates to use a CPIM or CPP gateway, RFC 3860 and RFC 3859

  • “ymsgr” indicates to use yahoo

  • “msn” might indicate to use Microsoft messenger

  • “aim” indicates to use AOL

# File lib/vpim/vcard.rb, line 1243
def add_impp(url) # :yield: impp
  params = {}

  if block_given?
    x = Struct.new( :location, :preferred, :purpose ).new

    yield x

    x[:preferred] = 'PREF' if x[:preferred]

    types = x.to_a.flatten.compact.map { |s| s.downcase }.uniq

    params['TYPE'] = types if types.first
  end

  @card << Vpim::DirectoryInfo::Field.create( 'IMPP', url, params)
  self
end
add_name()
Alias for: name
add_note(note) click to toggle source

Add a note field, NOTE. The note String can contain newlines, they will be escaped.

# File lib/vpim/vcard.rb, line 1213
def add_note(note)
  @card << Vpim::DirectoryInfo::Field.create( 'NOTE', Vpim.encode_text(note) );
end
add_photo() { |photo| ... } click to toggle source

Add a photo field, PHOTO.

Attributes of PHOTO are:

  • image: set to image data to include inline

  • link: set to the URL of the image data

  • type: string identifying the image type, supposed to be an “IANA registered image format”,

    or a non-registered image format (usually these start with an x-)

An error will be raised if neither image or link is set, or if both image and link is set.

Setting type is optional for a link image, because either the URL, the image file extension, or a HTTP Content-Type may specify the type. If it's not a link, setting type is mandatory, though it can be set to an empty string, '', if the type is unknown.

TODO - I'm not sure about this API. I'm thinking maybe it should be add_photo(image, type), and that I should detect when the image is a URL, and make type mandatory if it wasn't a URL.

# File lib/vpim/vcard.rb, line 1312
def add_photo # :yield: photo
  x = Struct.new(:image, :link, :type).new
  yield x
  if x[:image] && x[:link]
    raise Vpim::InvalidEncodingError, 'Image is not allowed to be both inline and a link.'
  end

  value = x[:image] || x[:link]

  if !value
    raise Vpim::InvalidEncodingError, 'A image link or inline data must be provided.'
  end

  params = {}

  # Don't set type to the empty string.
  params['TYPE'] = x[:type] if( x[:type] && x[:type].length > 0 )

  if x[:link]
    params['VALUE'] = 'URI'
  else # it's inline, base-64 encode it
    params['ENCODING'] = :b64
    if !x[:type]
      raise Vpim::InvalidEncodingError, 'Inline image data must have it\s type set.'
    end
  end

  @card << Vpim::DirectoryInfo::Field.create( 'PHOTO', value, params )
  self
end
add_tel(number) { |tel| ... } click to toggle source

Add a telephone field, TEL. tel is a Vpim::Vcard::Telephone.

The block is optional, its only necessary if you want to specify the optional attributes.

# File lib/vpim/vcard.rb, line 1166
def add_tel(number) # :yield: tel
  x = Vpim::Vcard::Telephone.new(number)
  if block_given?
    yield x
  end
  @card << x.encode
  self
end
add_url(url) click to toggle source

Add a URL field, URL.

# File lib/vpim/vcard.rb, line 1363
def add_url(url)
  @card << Vpim::DirectoryInfo::Field.create( 'URL', url.to_str );
end
add_x_aim(xaim) { |xaim| ... } click to toggle source

Add an X-AIM account name where xaim is an AIM screen name.

I don't know if this is conventional, or supported by anything other than AddressBook.app, but an example is:

X-AIM;type=HOME;type=pref:exampleaccount

Attributes of X-AIM are:

  • preferred: true - set if this is the preferred address

  • location: home, work, mobile - location of address

All attributes are optional, and so is the block.

# File lib/vpim/vcard.rb, line 1273
def add_x_aim(xaim) # :yield: xaim
  params = {}

  if block_given?
    x = Struct.new( :location, :preferred ).new

    yield x

    x[:preferred] = 'PREF' if x[:preferred]

    types = x.to_a.flatten.compact.map { |s| s.downcase }.uniq

    params['TYPE'] = types if types.first
  end

  @card << Vpim::DirectoryInfo::Field.create( 'X-AIM', xaim, params)
  self
end
birthday=(birthday) click to toggle source

Add a birthday field, BDAY.

birthday must be a time or date object.

Warning: It may confuse both humans and software if you add multiple birthdays.

# File lib/vpim/vcard.rb, line 1203
def birthday=(birthday)
  if !birthday.respond_to? :month
    raise ArgumentError, 'birthday must be a date or time object.'
  end
  delete_if { |l| l.name == 'BDAY' }
  @card << Vpim::DirectoryInfo::Field.create( 'BDAY', birthday );
end
copy(card) { |Field| ... } click to toggle source

Copy the fields from card into self using add_field. If a block is provided, each Field from card is yielded. The block should return a Field to add, or nil. The Field doesn't have to be the one yielded, allowing the field to be copied and modified (see Field#copy) before adding, or not added at all if the block yields nil.

The vCard fields BEGIN and END aren't copied, and VERSION, N, and FN are copied only if the card doesn't have them already.

# File lib/vpim/vcard.rb, line 1393
def copy(card) # :yields: Field
  card.each do |field|
    fieldname = field.name.upcase
    case
    when [ 'BEGIN', 'END' ].include?(fieldname)
      # Never copy these

    when [ 'VERSION', 'N', 'FN' ].include?(fieldname) && @card.field(fieldname)
      # Copy these only if they don't already exist.

    else
      if block_given?
        field = yield field
      end

      if field
        add_field(field)
      end
    end
  end
end
delete_if() { |line| ... } click to toggle source

Delete line if block yields true.

# File lib/vpim/vcard.rb, line 1416
def delete_if #:yield: line
  begin
  @card.delete_if do |line|
    yield line
  end
  rescue NoMethodError
    # FIXME - this is a hideous hack, allowing a DirectoryInfo to
    # be passed instead of a Vcard, and for it to almost work. Yuck.
  end
end
name() { |name| ... } click to toggle source

Set the name fields, N and FN.

Attributes of name are:

  • family: family name

  • given: given name

  • additional: additional names

  • prefix: such as “Ms.” or “Dr.”

  • suffix: such as “BFA”, or “Sensei”

name is a Vcard::Name.

All attributes are optional, though have all names be zero-length strings isn't really in the spirit of things. FN's value will be set to Vpim::Vcard::Name#formatted if Vpim::Vcard::Name#fullname isn't given a specific value.

Warning: This is the only mandatory field.

# File lib/vpim/vcard.rb, line 1129
def name #:yield:name
  x = begin
        @card.name.dup
      rescue
        Vpim::Vcard::Name.new
      end

  fn = x.fullname

  yield x

  x.fullname.strip!

  delete_if do |line|
    line.name == 'N'
  end

  @card << x.encode
  @card << x.encode_fn

  self
end
Also aliased as: add_name
nickname=(nickname) click to toggle source

Set the nickname field, NICKNAME.

It can be set to a single String or an Array of String.

# File lib/vpim/vcard.rb, line 1191
def nickname=(nickname)
  delete_if { |l| l.name == 'NICKNAME' }

  @card << Vpim::DirectoryInfo::Field.create( 'NICKNAME', nickname );
end
org=(org) click to toggle source

Set the org field, ORG.

It can be set to a single String or an Array of String.

# File lib/vpim/vcard.rb, line 1355
def org=(org)
  delete_if { |l| l.name == 'ORG' }

  @card << Vpim::DirectoryInfo::Field.create( 'ORG', org );
end
title=(title) click to toggle source

Set the title field, TITLE.

It can be set to a single String.

# File lib/vpim/vcard.rb, line 1346
def title=(title)
  delete_if { |l| l.name == 'TITLE' }

  @card << Vpim::DirectoryInfo::Field.create( 'TITLE', title );
end