Class: Vcard::Vcard::Maker

Inherits:
Object
  • Object
show all
Defined in:
lib/vcard/vcard.rb

Overview

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 Vcard::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:

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.make(full_name = nil, &block) ⇒ Object

Deprecated, use #make2.

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



1025
1026
1027
# File 'lib/vcard/vcard.rb', line 1025

def self.make(full_name = nil, &block) # :yields: maker
  new(full_name, ::Vcard::Vcard.create).make(&block)
end

.make2(card = ::Vcard::Vcard.create, &block) ⇒ Object

Make a vCard.

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

If card is nil or not provided a new Vcard::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.



1017
1018
1019
# File 'lib/vcard/vcard.rb', line 1017

def self.make2(card = ::Vcard::Vcard.create, &block) # :yields: maker
  new(nil, card).make(&block)
end

Instance Method Details

#add_addr {|x| ... } ⇒ Object

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

Yields:

  • (x)


1113
1114
1115
1116
1117
1118
# File 'lib/vcard/vcard.rb', line 1113

def add_addr # :yield: address
  x = ::Vcard::Vcard::Address.new
  yield x
  @card << x.encode
  self
end

#add_email(email) ⇒ Object

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

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



1137
1138
1139
1140
1141
1142
1143
1144
# File 'lib/vcard/vcard.rb', line 1137

def add_email(email) # :yield: email
  x = ::Vcard::Vcard::Email.new(email)
  if block_given?
    yield x
  end
  @card << x.encode
  self
end

#add_field(field) ⇒ Object

Add a Field, field.



1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
# File 'lib/vcard/vcard.rb', line 1332

def add_field(field)
  fieldname = field.name.upcase
  case
  when [ "BEGIN", "END" ].include?(fieldname)
    raise ::Vcard::InvalidEncodingError, "Not allowed to manually add #{field.name} to a vCard."

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

  else
    @card << field
  end
end

#add_impp(url) ⇒ Object

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



1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
# File 'lib/vcard/vcard.rb', line 1201

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 << ::Vcard::DirectoryInfo::Field.create( "IMPP", url, params)
  self
end

#add_note(note) ⇒ Object

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



1171
1172
1173
# File 'lib/vcard/vcard.rb', line 1171

def add_note(note)
  @card << ::Vcard::DirectoryInfo::Field.create( "NOTE", ::Vcard.encode_text(note) );
end

#add_photo {|x| ... } ⇒ Object

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.

Yields:

  • (x)


1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
# File 'lib/vcard/vcard.rb', line 1270

def add_photo # :yield: photo
  x = Struct.new(:image, :link, :type).new
  yield x
  if x[:image] && x[:link]
    raise ::Vcard::InvalidEncodingError, "Image is not allowed to be both inline and a link."
  end

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

  if !value
    raise ::Vcard::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 ::Vcard::InvalidEncodingError, "Inline image data must have it's type set."
    end
  end

  @card << ::Vcard::DirectoryInfo::Field.create( "PHOTO", value, params )
  self
end

#add_role(role) ⇒ Object

Add a role field, ROLE.

It can be set to a single String.



1322
1323
1324
# File 'lib/vcard/vcard.rb', line 1322

def add_role(role)
  @card << ::Vcard::DirectoryInfo::Field.create("ROLE", ::Vcard.encode_text(role));
end

#add_tel(number) ⇒ Object

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

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



1124
1125
1126
1127
1128
1129
1130
1131
# File 'lib/vcard/vcard.rb', line 1124

def add_tel(number) # :yield: tel
  x = ::Vcard::Vcard::Telephone.new(number)
  if block_given?
    yield x
  end
  @card << x.encode
  self
end

#add_url(url) ⇒ Object

Add a URL field, URL.



1327
1328
1329
# File 'lib/vcard/vcard.rb', line 1327

def add_url(url)
  @card << ::Vcard::DirectoryInfo::Field.create( "URL", url.to_str );
end

#add_x_aim(xaim) ⇒ Object

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.



1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
# File 'lib/vcard/vcard.rb', line 1231

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 << ::Vcard::DirectoryInfo::Field.create( "X-AIM", xaim, params)
  self
end

#birthday=(birthday) ⇒ Object

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.



1161
1162
1163
1164
1165
1166
1167
# File 'lib/vcard/vcard.rb', line 1161

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 << ::Vcard::DirectoryInfo::Field.create( "BDAY", birthday );
end

#copy(card) ⇒ Object

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.



1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
# File 'lib/vcard/vcard.rb', line 1357

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_ifObject

Delete line if block yields true.



1380
1381
1382
1383
1384
1385
1386
1387
# File 'lib/vcard/vcard.rb', line 1380

def delete_if #:yield: line
  @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

#fullname=(fullname) ⇒ Object

Deprecated, see #name.

Use

maker.name do |n| n.fullname = "foo" end

to set just fullname, or set the other fields to set fullname and the name.



1065
1066
1067
1068
1069
1070
# File 'lib/vcard/vcard.rb', line 1065

def fullname=(fullname) #:nodoc: bacwards compat
  if @card.field("FN")
    raise ::Vcard::InvalidEncodingError, "Not allowed to add more than one FN field to a vCard."
  end
  @card << ::Vcard::DirectoryInfo::Field.create( "FN", fullname );
end

#make {|_self| ... } ⇒ Object

:nodoc:

Yields:

  • (_self)

Yield Parameters:



1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
# File 'lib/vcard/vcard.rb', line 1029

def make # :nodoc:
  yield self
  unless @card["N"]
    raise ::Vcard::Unencodeable, "N field is mandatory"
  end
  fn = @card.field("FN")
  if fn && fn.value.strip.length == 0
    @card.delete(fn)
    fn = nil
  end
  unless fn
    @card << ::Vcard::DirectoryInfo::Field.create("FN", ::Vcard::Vcard::Name.new(@card["N"], "").formatted)
  end
  unless @card["VERSION"]
    @card << ::Vcard::DirectoryInfo::Field.create("VERSION", "3.0")
  end
  @card
end

#name {|x| ... } ⇒ Object Also known as: add_name

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 Vcard::Name#formatted if Vcard::Name#fullname isn’t given a specific value.

Warning: This is the only mandatory field.

Yields:

  • (x)


1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
# File 'lib/vcard/vcard.rb', line 1089

def name #:yield:name
  x = begin
        @card.name.dup
      rescue
        ::Vcard::Vcard::Name.new
      end

  yield x

  x.fullname.strip!

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

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

  self
end

#nickname=(nickname) ⇒ Object

Set the nickname field, NICKNAME.

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



1149
1150
1151
1152
1153
# File 'lib/vcard/vcard.rb', line 1149

def nickname=(nickname)
  delete_if { |l| l.name == "NICKNAME" }

  @card << ::Vcard::DirectoryInfo::Field.create( "NICKNAME", nickname );
end

#org=(org) ⇒ Object

Set the org field, ORG.

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



1313
1314
1315
1316
1317
# File 'lib/vcard/vcard.rb', line 1313

def org=(org)
  delete_if { |l| l.name == "ORG" }

  @card << ::Vcard::DirectoryInfo::Field.create( "ORG", org );
end

#title=(title) ⇒ Object

Set the title field, TITLE.

It can be set to a single String.



1304
1305
1306
1307
1308
# File 'lib/vcard/vcard.rb', line 1304

def title=(title)
  delete_if { |l| l.name == "TITLE" }

  @card << ::Vcard::DirectoryInfo::Field.create( "TITLE", title );
end