Module: Virginity::VcardCleaning

Included in:
Vcard
Defined in:
lib/virginity/vcard/cleaning.rb

Constant Summary collapse

VERSION30 =

make sure there is exactly one version-field that says “3.0” and do that in a smart way so the vcard is not changed if it’s not nescessary

"VERSION:3.0"
VERSION =
"VERSION"
X_ABUID =
"X-ABUID"
X_IRMC_LUID =

Sony-Ericsson phones send those. They don’t mean anything for us. They are meant for Windows software that syncs the desktop with a phone afaik.

"X-IRMC-LUID"
NAME =

since we could have received a vcard with too many semicolons

'N'
CATEGORIES =

concatenate all CATEGORIES-lines, make categories unique and sorted

"CATEGORIES"
NICKNAME =

after unpacking there should be one nickname value per NICKNAME the order should not change.

"NICKNAME"
RSTRIPPABLE_FIELDS =
/^(EMAIL|FN|IMPP|NOTE|TEL|URL)$/i

Instance Method Summary collapse

Instance Method Details

#clean!Object Also known as: super_clean!

run almost every clean method on whole vcards and on separate fields (FieldCleaning), in a correct order



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
# File 'lib/virginity/vcard/cleaning.rb', line 115

def clean!
  clean_version!
  remove_x_abuids!
  remove_x_irmc_luids!
  rstrip_text_fields!
#       strip_structured_fields! # mh, better not do this here. It's too much
  remove_empty_fields!
  fields.each { |field| field.clean! }
  clean_categories!
  unpack_nicknames!
  clean_orgs!
  max_one_name!
  clean_name!
  clean_dates!
  clean_adrs!
  convert_xabadrs_to_param!
  convert_xablabels_to_param!
  remove_duplicate_lines!
  remove_singleton_groups!
  clean_same_value_fields!
  remove_subset_addresses!
  reset_empty_formatted_name!
  self
end

#clean_adrs!Object



358
359
360
361
362
# File 'lib/virginity/vcard/cleaning.rb', line 358

def clean_adrs!
  lines_with_name("ADR").each do |adr|
    adr.reencode! if EncodingDecoding::decode_text_list(adr.raw_value, ";").size != 7
  end
end

#clean_categories!Object



210
211
212
213
214
215
216
# File 'lib/virginity/vcard/cleaning.rb', line 210

def clean_categories!
  clean_multivalue_fields!(CATEGORIES) # concat
  if categories = lines_with_name(CATEGORIES).first
    categories.values = categories.values.map { |c| c.strip }.sort.uniq
  end
  self
end

#clean_dates!Object



348
349
350
351
352
353
354
355
356
# File 'lib/virginity/vcard/cleaning.rb', line 348

def clean_dates!
  (birthdays + anniversaries + dates).each do |day|
    begin
      day.date = day.date.to_s # normalize the date format
    rescue => e
      nil
    end
  end
end

#clean_multivalue_fields!(name) ⇒ Object

make one field containing all of values of fields with the same name



198
199
200
201
202
203
204
205
206
# File 'lib/virginity/vcard/cleaning.rb', line 198

def clean_multivalue_fields!(name)
  fields = lines_with_name(name)
  while fields.size > 1
    last = fields.pop
    fields.first.values.concat( last.values.to_a )
    delete_field(last)
  end
  self
end

#clean_name!Object



184
185
186
187
# File 'lib/virginity/vcard/cleaning.rb', line 184

def clean_name!
  lines_with_name(NAME).each { |n| n.reencode! }
  self
end

#clean_orgs!Object

why? to remove trailing semicolons like in: “ORG:foo;bar;”



168
169
170
171
# File 'lib/virginity/vcard/cleaning.rb', line 168

def clean_orgs!
  organizations.each { |org| org.reencode!(:variable_number_of_fields => true) }
  self
end

#clean_same_value_fields!Object

merge fields with the same value (collect all params in the remaining field)



280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/virginity/vcard/cleaning.rb', line 280

def clean_same_value_fields!
  fields.each do |upper|
    next if Vcard::SINGLETON_FIELDS.include?(upper.name)
    fields.each do |lower|
      next if lower.object_id == upper.object_id # nescessary
      next unless lower.name == upper.name # optimisation
      next unless lower.raw_value == upper.raw_value # optimisation
      begin
#             puts "merging #{upper} and #{lower}"
        upper.merge_with!(lower)
        delete_field(lower)
#             puts "merged: #{upper}"
      rescue # nescessary
      end
    end
  end
  self
end

#clean_version!Object



145
146
147
148
149
150
151
# File 'lib/virginity/vcard/cleaning.rb', line 145

def clean_version!
  unless (self/VERSION30).size == 1
    lines_with_name(VERSION).each {|f| delete_field(f) }
    self << VERSION30
  end
  self
end

#convert_custom_osx_field_to_param!(fields) ⇒ Object



255
256
257
258
259
260
261
262
263
264
# File 'lib/virginity/vcard/cleaning.rb', line 255

def convert_custom_osx_field_to_param!(fields)
  fields.each do |custom|
    unless custom.group.nil?
      same_group = where(:group => custom.group) - [custom]
      same_group.each { |field| field.params << custom.to_param }
    end
    delete_field(custom)
  end
  self
end

#convert_xabadrs_to_param!Object



266
267
268
# File 'lib/virginity/vcard/cleaning.rb', line 266

def convert_xabadrs_to_param!
  convert_custom_osx_field_to_param! lines_with_name("X-ABADR")
end

#convert_xablabels_to_param!Object



270
271
272
# File 'lib/virginity/vcard/cleaning.rb', line 270

def convert_xablabels_to_param!
  convert_custom_osx_field_to_param! lines_with_name("X-ABLabel")
end

#max_one_name!Object



175
176
177
178
179
180
181
182
# File 'lib/virginity/vcard/cleaning.rb', line 175

def max_one_name!
  n_fields = lines_with_name(NAME)
  return self unless n_fields.size > 1
  # remove all N fields except the biggest
  n_fields.delete(n_fields.max_by { |f| f.raw_value.size })
  delete(*n_fields)
  self
end

#remove_duplicate_lines!Object



274
275
276
277
# File 'lib/virginity/vcard/cleaning.rb', line 274

def remove_duplicate_lines!
  fields.uniq!
  self
end

#remove_empty_fields!Object

there’s no use in keeping fields without a value



190
191
192
193
194
195
# File 'lib/virginity/vcard/cleaning.rb', line 190

def remove_empty_fields!
  lines.delete_if do |x|
    x.raw_value.strip.empty? or (x.respond_to? :values and x.values.join.empty?)
  end
  self
end

#remove_extra_logos!Object



328
329
330
# File 'lib/virginity/vcard/cleaning.rb', line 328

def remove_extra_logos!
  remove_extra_photos!(logos)
end

#remove_extra_photos!(p = photos) ⇒ Object



322
323
324
325
326
# File 'lib/virginity/vcard/cleaning.rb', line 322

def remove_extra_photos!(p = photos)
  p.shift
  p.each { |line| delete_field(line) }
  self
end

#remove_singleton_groups!Object

if there’s only one field in a certain group that group can be reset



246
247
248
249
250
251
252
253
# File 'lib/virginity/vcard/cleaning.rb', line 246

def remove_singleton_groups!
  groups = fields.map {|f| f.group }.compact!
  fields.each do |field|
    next if field.group.nil?
    field.group = nil if groups.select { |g| g == field.group }.size == 1
  end
  self
end

#remove_subset_addresses!Object



313
314
315
# File 'lib/virginity/vcard/cleaning.rb', line 313

def remove_subset_addresses!
  remove_subsets_of_structured_fields!(addresses)
end

#remove_subsets_of_structured_fields!(fields) ⇒ Object

removes fields that are a subset of another existing field

In the following example, field two is a subset of field one.

one = Field.new("ADR;TYPE=HOME:;;Bickerson Street 4;Dudley Town;;6215 AX;NeverNeverland")
two = Field.new("ADR;TYPE=HOME:;;Bickerson Street 4;Dudley Town;;;")
two.subset_of?(one) ==> true


305
306
307
308
309
310
311
# File 'lib/virginity/vcard/cleaning.rb', line 305

def remove_subsets_of_structured_fields!(fields)
  fields = fields.dup
  while field = fields.pop
    delete_field(field) if fields.any? {|other| field.subset_of?(other) }
  end
  self
end

#remove_x_abuids!Object

OS-X is not sending those anymore, but existing contact can still contain those fields



155
156
157
158
# File 'lib/virginity/vcard/cleaning.rb', line 155

def remove_x_abuids!
  lines_with_name(X_ABUID).each {|f| delete_field(f) }
  self
end

#remove_x_irmc_luids!Object



162
163
164
165
# File 'lib/virginity/vcard/cleaning.rb', line 162

def remove_x_irmc_luids!
  lines_with_name(X_IRMC_LUID).each {|f| delete_field(f) }
  self
end

#reset_empty_formatted_name!Object



317
318
319
320
# File 'lib/virginity/vcard/cleaning.rb', line 317

def reset_empty_formatted_name!
  name.reset_formatted!
  self
end

#rstrip_text_fields!Object

remove right hand whitespace from the values of all RSTRIPPABLE_FIELDS



334
335
336
337
338
339
# File 'lib/virginity/vcard/cleaning.rb', line 334

def rstrip_text_fields!
  fields.each do |field|
    field.raw_value.rstrip! if field.name =~ RSTRIPPABLE_FIELDS
  end
  self
end

#strip_structured_fields!Object

remove whitespace around parts of a name



342
343
344
345
346
# File 'lib/virginity/vcard/cleaning.rb', line 342

def strip_structured_fields!
  (lines_with_name(NAME) + organizations + addresses).each do |field|
    field.components.each { |component| field.send(component.to_s+'=', field.send(component).strip) }
  end
end

#unpack_categories!Object

after unpacking there should be one categories lines per category the order should not change, but that doesn’t actually matter for categories



234
235
236
# File 'lib/virginity/vcard/cleaning.rb', line 234

def unpack_categories!
  unpack_list!(CATEGORIES)
end

#unpack_field!(field) ⇒ Object

after unpacking there SHOULD be one field for each value the order MUST NOT change.



220
221
222
223
224
# File 'lib/virginity/vcard/cleaning.rb', line 220

def unpack_field!(field)
  return if field.values.size == 1
  field.unpacked.each {|f| add_field(f) }
  delete_field(field)
end

#unpack_list!(list_name) ⇒ Object



226
227
228
229
230
# File 'lib/virginity/vcard/cleaning.rb', line 226

def unpack_list!(list_name)
  lines_with_name(list_name).each { |field| unpack_field!(field) }
  remove_empty_fields!
  self
end

#unpack_nicknames!Object



241
242
243
# File 'lib/virginity/vcard/cleaning.rb', line 241

def unpack_nicknames!
  unpack_list!(NICKNAME)
end