vcard 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -0
- data/.gitignore +7 -0
- data/LICENSE +58 -0
- data/README.rdoc +7 -0
- data/Rakefile +58 -0
- data/VERSION +1 -0
- data/lib/vcard.rb +34 -0
- data/lib/vcard/attachment.rb +100 -0
- data/lib/vcard/dirinfo.rb +272 -0
- data/lib/vcard/enumerator.rb +30 -0
- data/lib/vcard/field.rb +610 -0
- data/lib/vcard/rfc2425.rb +367 -0
- data/lib/vcard/vcard.rb +1423 -0
- data/test/field_test.rb +152 -0
- data/test/test_helper.rb +10 -0
- data/test/vcard_test.rb +967 -0
- metadata +73 -0
data/.document
ADDED
data/.gitignore
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
vPim is copyrighted free software by Sam Roberts <sroberts@uniserve.com>.
|
2
|
+
|
3
|
+
You can redistribute it and/or modify it under either the terms of the GPL (see
|
4
|
+
the file GPL), or the conditions below:
|
5
|
+
|
6
|
+
1. You may make and give away verbatim copies of the source form of the
|
7
|
+
software without restriction, provided that you duplicate all of the
|
8
|
+
original copyright notices and associated disclaimers.
|
9
|
+
|
10
|
+
2. You may modify your copy of the software in any way, provided that
|
11
|
+
you do at least ONE of the following:
|
12
|
+
|
13
|
+
a) place your modifications in the Public Domain or otherwise make them
|
14
|
+
Freely Available, such as by posting said modifications to Usenet or an
|
15
|
+
equivalent medium, or by allowing the author to include your
|
16
|
+
modifications in the software.
|
17
|
+
|
18
|
+
b) use the modified software only within your corporation or
|
19
|
+
organization.
|
20
|
+
|
21
|
+
c) give non-standard binaries non-standard names, with instructions on
|
22
|
+
where to get the original software distribution.
|
23
|
+
|
24
|
+
d) make other distribution arrangements with the author.
|
25
|
+
|
26
|
+
3. You may distribute the software in object code or binary form,
|
27
|
+
provided that you do at least ONE of the following:
|
28
|
+
|
29
|
+
a) distribute the binaries and library files of the software, together
|
30
|
+
with instructions (in the manual page or equivalent) on where to get the
|
31
|
+
original distribution.
|
32
|
+
|
33
|
+
b) accompany the distribution with the machine-readable source of the
|
34
|
+
software.
|
35
|
+
|
36
|
+
c) give non-standard binaries non-standard names, with instructions on
|
37
|
+
where to get the original software distribution.
|
38
|
+
|
39
|
+
d) make other distribution arrangements with the author.
|
40
|
+
|
41
|
+
4. You may modify and include the part of the software into any other
|
42
|
+
software (possibly commercial). But some files in the distribution
|
43
|
+
are not written by the author, so that they are not under these terms.
|
44
|
+
|
45
|
+
For the list of those files and their copying conditions, see the
|
46
|
+
file LEGAL.
|
47
|
+
|
48
|
+
5. The scripts and library files supplied as input to or produced as
|
49
|
+
output from the software do not automatically fall under the
|
50
|
+
copyright of the software, but belong to whomever generated them,
|
51
|
+
and may be sold commercially, and may be aggregated with this
|
52
|
+
software.
|
53
|
+
|
54
|
+
6. THIS SOFTWARE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR
|
55
|
+
IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
|
56
|
+
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
57
|
+
PURPOSE.
|
58
|
+
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rake'
|
5
|
+
|
6
|
+
begin
|
7
|
+
require 'jeweler'
|
8
|
+
Jeweler::Tasks.new do |gem|
|
9
|
+
gem.name = "vcard"
|
10
|
+
gem.summary = %Q{Vcard support extracted from Vpim (Ruby 1.9.1 compatible)}
|
11
|
+
gem.email = "qoobaa@gmail.com"
|
12
|
+
gem.homepage = "http://github.com/qoobaa/vcard"
|
13
|
+
gem.authors = ["Jakub Kuźma"]
|
14
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
15
|
+
end
|
16
|
+
|
17
|
+
rescue LoadError
|
18
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
19
|
+
end
|
20
|
+
|
21
|
+
require 'rake/testtask'
|
22
|
+
Rake::TestTask.new(:test) do |test|
|
23
|
+
test.libs << 'lib' << 'test'
|
24
|
+
test.pattern = 'test/**/*_test.rb'
|
25
|
+
test.verbose = true
|
26
|
+
end
|
27
|
+
|
28
|
+
begin
|
29
|
+
require 'rcov/rcovtask'
|
30
|
+
Rcov::RcovTask.new do |test|
|
31
|
+
test.libs << 'test'
|
32
|
+
test.pattern = 'test/**/*_test.rb'
|
33
|
+
test.verbose = true
|
34
|
+
end
|
35
|
+
rescue LoadError
|
36
|
+
task :rcov do
|
37
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'rake/rdoctask'
|
45
|
+
Rake::RDocTask.new do |rdoc|
|
46
|
+
if File.exist?('VERSION.yml')
|
47
|
+
config = YAML.load(File.read('VERSION.yml'))
|
48
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
49
|
+
else
|
50
|
+
version = ""
|
51
|
+
end
|
52
|
+
|
53
|
+
rdoc.rdoc_dir = 'rdoc'
|
54
|
+
rdoc.title = "vcard #{version}"
|
55
|
+
rdoc.rdoc_files.include('README*')
|
56
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
57
|
+
end
|
58
|
+
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.1.1
|
data/lib/vcard.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
#:main:README
|
10
|
+
#:title:vPim - vCard and iCalendar support for Ruby
|
11
|
+
module Vpim
|
12
|
+
# Exception used to indicate that data being decoded is invalid, the message
|
13
|
+
# should describe what is invalid.
|
14
|
+
class InvalidEncodingError < StandardError; end
|
15
|
+
|
16
|
+
# Exception used to indicate that data being decoded is unsupported, the message
|
17
|
+
# should describe what is unsupported.
|
18
|
+
#
|
19
|
+
# If its unsupported, its likely because I didn't anticipate it being useful
|
20
|
+
# to support this, and it likely it could be supported on request.
|
21
|
+
class UnsupportedError < StandardError; end
|
22
|
+
|
23
|
+
# Exception used to indicate that encoding failed, probably because the
|
24
|
+
# object would not result in validly encoded data. The message should
|
25
|
+
# describe what is unsupported.
|
26
|
+
class Unencodeable < StandardError; end
|
27
|
+
end
|
28
|
+
|
29
|
+
require "vcard/attachment"
|
30
|
+
require "vcard/dirinfo"
|
31
|
+
require "vcard/enumerator"
|
32
|
+
require "vcard/field"
|
33
|
+
require "vcard/rfc2425"
|
34
|
+
require "vcard/vcard"
|
@@ -0,0 +1,100 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Vpim
|
10
|
+
|
11
|
+
# Attachments are used by both iCalendar and vCard. They are either a URI or
|
12
|
+
# inline data, and their decoded value will be either a Uri or a Inline, as
|
13
|
+
# appropriate.
|
14
|
+
#
|
15
|
+
# Besides the methods specific to their class, both kinds of object implement
|
16
|
+
# a set of common methods, allowing them to be treated uniformly:
|
17
|
+
# - Uri#to_io, Inline#to_io: return an IO from which the value can be read.
|
18
|
+
# - Uri#to_s, Inline#to_s: return the value as a String.
|
19
|
+
# - Uri#format, Inline#format: the format of the value. This is supposed to
|
20
|
+
# be an "iana defined" identifier (like "image/jpeg"), but could be almost
|
21
|
+
# anything (or nothing) in practice. Since the parameter is optional, it may
|
22
|
+
# be "".
|
23
|
+
#
|
24
|
+
# The objects can also be distinguished by their class, if necessary.
|
25
|
+
module Attachment
|
26
|
+
|
27
|
+
# TODO - It might be possible to autodetect the format from the first few
|
28
|
+
# bytes of the value, and return the appropriate MIME type when format
|
29
|
+
# isn't defined.
|
30
|
+
#
|
31
|
+
# iCalendar and vCard put the format in different parameters, and the
|
32
|
+
# default kind of value is different.
|
33
|
+
def Attachment.decode(field, defkind, fmtparam) #:nodoc:
|
34
|
+
format = field.pvalue(fmtparam) || ''
|
35
|
+
kind = field.kind || defkind
|
36
|
+
case kind
|
37
|
+
when 'text'
|
38
|
+
Inline.new(Vpim.decode_text(field.value), format)
|
39
|
+
when 'uri'
|
40
|
+
Uri.new(field.value_raw, format)
|
41
|
+
when 'binary'
|
42
|
+
Inline.new(field.value, format)
|
43
|
+
else
|
44
|
+
raise InvalidEncodingError, "Attachment of type #{kind} is not allowed"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# Extends a String to support some of the same methods as Uri.
|
49
|
+
class Inline < String
|
50
|
+
def initialize(s, format) #:nodoc:
|
51
|
+
@format = format
|
52
|
+
super(s)
|
53
|
+
end
|
54
|
+
|
55
|
+
# Return an IO object for the inline data. See +stringio+ for more
|
56
|
+
# information.
|
57
|
+
def to_io
|
58
|
+
StringIO.new(self)
|
59
|
+
end
|
60
|
+
|
61
|
+
# The format of the inline data.
|
62
|
+
# See Attachment.
|
63
|
+
attr_reader :format
|
64
|
+
end
|
65
|
+
|
66
|
+
# Encapsulates a URI and implements some methods of String.
|
67
|
+
class Uri
|
68
|
+
def initialize(uri, format) #:nodoc:
|
69
|
+
@uri = uri
|
70
|
+
@format = format
|
71
|
+
end
|
72
|
+
|
73
|
+
# The URI value.
|
74
|
+
attr_reader :uri
|
75
|
+
|
76
|
+
# The format of the data referred to by the URI.
|
77
|
+
# See Attachment.
|
78
|
+
attr_reader :format
|
79
|
+
|
80
|
+
# Return an IO object from opening the URI. See +open-uri+ for more
|
81
|
+
# information.
|
82
|
+
def to_io
|
83
|
+
open(@uri)
|
84
|
+
end
|
85
|
+
|
86
|
+
# Return the String from reading the IO object to end-of-data.
|
87
|
+
def to_s
|
88
|
+
to_io.read(nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
def inspect #:nodoc:
|
92
|
+
s = "<#{self.class.to_s}: #{uri.inspect}>"
|
93
|
+
s << ", #{@format.inspect}" if @format
|
94
|
+
s
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
@@ -0,0 +1,272 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2008 Sam Roberts
|
3
|
+
|
4
|
+
This library is free software; you can redistribute it and/or modify it
|
5
|
+
under the same terms as the ruby language itself, see the file COPYING for
|
6
|
+
details.
|
7
|
+
=end
|
8
|
+
|
9
|
+
module Vpim
|
10
|
+
# An RFC 2425 directory info object.
|
11
|
+
#
|
12
|
+
# A directory information object is a sequence of fields. The basic
|
13
|
+
# structure of the object, and the way in which it is broken into fields
|
14
|
+
# is common to all profiles of the directory info type.
|
15
|
+
#
|
16
|
+
# A vCard, for example, is a specialization of a directory info object.
|
17
|
+
#
|
18
|
+
# - [RFC2425] the directory information framework (ftp://ftp.ietf.org/rfc/rfc2425.txt)
|
19
|
+
#
|
20
|
+
# Here's an example of encoding a simple vCard using the low-level APIs:
|
21
|
+
#
|
22
|
+
# card = Vpim::Vcard.create
|
23
|
+
# card << Vpim::DirectoryInfo::Field.create('EMAIL', 'user.name@example.com', 'TYPE' => 'INTERNET' )
|
24
|
+
# card << Vpim::DirectoryInfo::Field.create('URL', 'http://www.example.com/user' )
|
25
|
+
# card << Vpim::DirectoryInfo::Field.create('FN', 'User Name' )
|
26
|
+
# puts card.to_s
|
27
|
+
#
|
28
|
+
# Don't do it like that, use Vpim::Vcard::Maker.
|
29
|
+
class DirectoryInfo
|
30
|
+
include Enumerable
|
31
|
+
|
32
|
+
private_class_method :new
|
33
|
+
|
34
|
+
# Initialize a DirectoryInfo object from +fields+. If +profile+ is
|
35
|
+
# specified, check the BEGIN/END fields.
|
36
|
+
def initialize(fields, profile = nil) #:nodoc:
|
37
|
+
if fields.detect { |f| ! f.kind_of? DirectoryInfo::Field }
|
38
|
+
raise ArgumentError, 'fields must be an array of DirectoryInfo::Field objects'
|
39
|
+
end
|
40
|
+
|
41
|
+
@string = nil # this is used as a flag to indicate that recoding will be necessary
|
42
|
+
@fields = fields
|
43
|
+
|
44
|
+
check_begin_end(profile) if profile
|
45
|
+
end
|
46
|
+
|
47
|
+
# Decode +card+ into a DirectoryInfo object.
|
48
|
+
#
|
49
|
+
# +card+ may either be a something that is convertible to a string using
|
50
|
+
# #to_str or an Array of objects that can be joined into a string using
|
51
|
+
# #join("\n"), or an IO object (which will be read to end-of-file).
|
52
|
+
#
|
53
|
+
# The lines in the string may be delimited using IETF (CRLF) or Unix (LF) conventions.
|
54
|
+
#
|
55
|
+
# A DirectoryInfo is mutable, you can add new fields to it, see
|
56
|
+
# Vpim::DirectoryInfo::Field#create() for how to create a new Field.
|
57
|
+
#
|
58
|
+
# TODO: I don't believe this is ever used, maybe I can remove it.
|
59
|
+
def DirectoryInfo.decode(card) #:nodoc:
|
60
|
+
if card.respond_to? :to_str
|
61
|
+
string = card.to_str
|
62
|
+
elsif card.kind_of? Array
|
63
|
+
string = card.join("\n")
|
64
|
+
elsif card.kind_of? IO
|
65
|
+
string = card.read(nil)
|
66
|
+
else
|
67
|
+
raise ArgumentError, "DirectoryInfo cannot be created from a #{card.type}"
|
68
|
+
end
|
69
|
+
|
70
|
+
fields = Vpim.decode(string)
|
71
|
+
|
72
|
+
new(fields)
|
73
|
+
end
|
74
|
+
|
75
|
+
# Create a new DirectoryInfo object. The +fields+ are an optional array of
|
76
|
+
# DirectoryInfo::Field objects to add to the new object, between the
|
77
|
+
# BEGIN/END. If the +profile+ string is not nil, then it is the name of
|
78
|
+
# the directory info profile, and the BEGIN:+profile+/END:+profile+ fields
|
79
|
+
# will be added.
|
80
|
+
#
|
81
|
+
# A DirectoryInfo is mutable, you can add new fields to it using #push(),
|
82
|
+
# and see Field#create().
|
83
|
+
def DirectoryInfo.create(fields = [], profile = nil)
|
84
|
+
|
85
|
+
if profile
|
86
|
+
p = profile.to_str
|
87
|
+
f = [ Field.create('BEGIN', p) ]
|
88
|
+
f.concat fields
|
89
|
+
f.push Field.create('END', p)
|
90
|
+
fields = f
|
91
|
+
end
|
92
|
+
|
93
|
+
new(fields, profile)
|
94
|
+
end
|
95
|
+
|
96
|
+
# The first field named +name+, or nil if no
|
97
|
+
# match is found.
|
98
|
+
def field(name)
|
99
|
+
enum_by_name(name).each { |f| return f }
|
100
|
+
nil
|
101
|
+
end
|
102
|
+
|
103
|
+
# The value of the first field named +name+, or nil if no
|
104
|
+
# match is found.
|
105
|
+
def [](name)
|
106
|
+
enum_by_name(name).each { |f| return f.value if f.value != ''}
|
107
|
+
enum_by_name(name).each { |f| return f.value }
|
108
|
+
nil
|
109
|
+
end
|
110
|
+
|
111
|
+
# An array of all the values of fields named +name+, converted to text
|
112
|
+
# (using Field#to_text()).
|
113
|
+
#
|
114
|
+
# TODO - call this #texts(), as in the plural?
|
115
|
+
def text(name)
|
116
|
+
accum = []
|
117
|
+
each do |f|
|
118
|
+
if f.name? name
|
119
|
+
accum << f.to_text
|
120
|
+
end
|
121
|
+
end
|
122
|
+
accum
|
123
|
+
end
|
124
|
+
|
125
|
+
# Array of all the Field#group()s.
|
126
|
+
def groups
|
127
|
+
@fields.collect { |f| f.group } .compact.uniq
|
128
|
+
end
|
129
|
+
|
130
|
+
# All fields, frozen.
|
131
|
+
def fields #:nodoc:
|
132
|
+
@fields.dup.freeze
|
133
|
+
end
|
134
|
+
|
135
|
+
# Yields for each Field for which +cond+.call(field) is true. The
|
136
|
+
# (default) +cond+ of nil is considered true for all fields, so
|
137
|
+
# this acts like a normal #each() when called with no arguments.
|
138
|
+
def each(cond = nil) # :yields: Field
|
139
|
+
@fields.each do |field|
|
140
|
+
if(cond == nil || cond.call(field))
|
141
|
+
yield field
|
142
|
+
end
|
143
|
+
end
|
144
|
+
self
|
145
|
+
end
|
146
|
+
|
147
|
+
# Returns an Enumerator for each Field for which #name?(+name+) is true.
|
148
|
+
#
|
149
|
+
# An Enumerator supports all the methods of Enumerable, so it allows iteration,
|
150
|
+
# collection, mapping, etc.
|
151
|
+
#
|
152
|
+
# Examples:
|
153
|
+
#
|
154
|
+
# Print all the nicknames in a card:
|
155
|
+
#
|
156
|
+
# card.enum_by_name('NICKNAME') { |f| puts f.value }
|
157
|
+
#
|
158
|
+
# Print an Array of the preferred email addresses in the card:
|
159
|
+
#
|
160
|
+
# pref_emails = card.enum_by_name('EMAIL').select { |f| f.pref? }
|
161
|
+
def enum_by_name(name)
|
162
|
+
Enumerator.new(self, Proc.new { |field| field.name?(name) })
|
163
|
+
end
|
164
|
+
|
165
|
+
# Returns an Enumerator for each Field for which #group?(+group+) is true.
|
166
|
+
#
|
167
|
+
# For example, to print all the fields, sorted by group, you could do:
|
168
|
+
#
|
169
|
+
# card.groups.sort.each do |group|
|
170
|
+
# card.enum_by_group(group).each do |field|
|
171
|
+
# puts "#{group} -> #{field.name}"
|
172
|
+
# end
|
173
|
+
# end
|
174
|
+
#
|
175
|
+
# or to get an array of all the fields in group 'AGROUP', you could do:
|
176
|
+
#
|
177
|
+
# card.enum_by_group('AGROUP').to_a
|
178
|
+
def enum_by_group(group)
|
179
|
+
Enumerator.new(self, Proc.new { |field| field.group?(group) })
|
180
|
+
end
|
181
|
+
|
182
|
+
# Returns an Enumerator for each Field for which +cond+.call(field) is true.
|
183
|
+
def enum_by_cond(cond)
|
184
|
+
Enumerator.new(self, cond )
|
185
|
+
end
|
186
|
+
|
187
|
+
# Force card to be reencoded from the fields.
|
188
|
+
def dirty #:nodoc:
|
189
|
+
#string = nil
|
190
|
+
end
|
191
|
+
|
192
|
+
# Append +field+ to the fields. Note that it won't be literally appended
|
193
|
+
# to the fields, it will be inserted before the closing END field.
|
194
|
+
def push(field)
|
195
|
+
dirty
|
196
|
+
@fields[-1,0] = field
|
197
|
+
self
|
198
|
+
end
|
199
|
+
|
200
|
+
alias << push
|
201
|
+
|
202
|
+
# Push +field+ onto the fields, unless there is already a field
|
203
|
+
# with this name.
|
204
|
+
def push_unique(field)
|
205
|
+
push(field) unless @fields.detect { |f| f.name? field.name }
|
206
|
+
self
|
207
|
+
end
|
208
|
+
|
209
|
+
# Append +field+ to the end of all the fields. This isn't usually what you
|
210
|
+
# want to do, usually a DirectoryInfo's first and last fields are a
|
211
|
+
# BEGIN/END pair, see #push().
|
212
|
+
def push_end(field)
|
213
|
+
@fields << field
|
214
|
+
self
|
215
|
+
end
|
216
|
+
|
217
|
+
# Delete +field+.
|
218
|
+
#
|
219
|
+
# Warning: You can't delete BEGIN: or END: fields, but other
|
220
|
+
# profile-specific fields can be deleted, including mandatory ones. For
|
221
|
+
# vCards in particular, in order to avoid destroying them, I suggest
|
222
|
+
# creating a new Vcard, and copying over all the fields that you still
|
223
|
+
# want, rather than using #delete. This is easy with Vcard::Maker#copy, see
|
224
|
+
# the Vcard::Maker examples.
|
225
|
+
def delete(field)
|
226
|
+
case
|
227
|
+
when field.name?('BEGIN'), field.name?('END')
|
228
|
+
raise ArgumentError, 'Cannot delete BEGIN or END fields.'
|
229
|
+
else
|
230
|
+
@fields.delete field
|
231
|
+
end
|
232
|
+
|
233
|
+
self
|
234
|
+
end
|
235
|
+
|
236
|
+
# The string encoding of the DirectoryInfo. See Field#encode for information
|
237
|
+
# about the width parameter.
|
238
|
+
def encode(width=nil)
|
239
|
+
unless @string
|
240
|
+
@string = @fields.collect { |f| f.encode(width) } . join ""
|
241
|
+
end
|
242
|
+
@string
|
243
|
+
end
|
244
|
+
|
245
|
+
alias to_s encode
|
246
|
+
|
247
|
+
# Check that the DirectoryInfo object is correctly delimited by a BEGIN
|
248
|
+
# and END, that their profile values match, and if +profile+ is specified, that
|
249
|
+
# they are the specified profile.
|
250
|
+
def check_begin_end(profile=nil) #:nodoc:
|
251
|
+
unless @fields.first
|
252
|
+
raise "No fields to check"
|
253
|
+
end
|
254
|
+
unless @fields.first.name? 'BEGIN'
|
255
|
+
raise "Needs BEGIN, found: #{@fields.first.encode nil}"
|
256
|
+
end
|
257
|
+
unless @fields.last.name? 'END'
|
258
|
+
raise "Needs END, found: #{@fields.last.encode nil}"
|
259
|
+
end
|
260
|
+
unless @fields.last.value? @fields.first.value
|
261
|
+
raise "BEGIN/END mismatch: (#{@fields.first.value} != #{@fields.last.value}"
|
262
|
+
end
|
263
|
+
if profile
|
264
|
+
if ! @fields.first.value? profile
|
265
|
+
raise "Mismatched profile"
|
266
|
+
end
|
267
|
+
end
|
268
|
+
true
|
269
|
+
end
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|