vcardigan 0.0.2
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/lib/vcardigan/errors.rb +3 -0
- data/lib/vcardigan/properties/name_property.rb +17 -0
- data/lib/vcardigan/property.rb +222 -0
- data/lib/vcardigan/vcard.rb +165 -0
- data/lib/vcardigan/version.rb +5 -0
- data/lib/vcardigan.rb +21 -0
- data/spec/examples/property_spec.rb +122 -0
- data/spec/examples/vcard_spec.rb +250 -0
- data/spec/spec_helper.rb +1 -0
- metadata +74 -0
@@ -0,0 +1,222 @@
|
|
1
|
+
module VCardigan
|
2
|
+
|
3
|
+
class Property
|
4
|
+
|
5
|
+
APPLE_LABEL = /_\$!<(.*?)>!\$_/
|
6
|
+
|
7
|
+
attr_accessor :group
|
8
|
+
attr_reader :name
|
9
|
+
attr_reader :params
|
10
|
+
attr_reader :values
|
11
|
+
|
12
|
+
def initialize(vcard, name, *args)
|
13
|
+
@vcard = vcard
|
14
|
+
@params = {}
|
15
|
+
@values = []
|
16
|
+
@group = nil
|
17
|
+
|
18
|
+
setup if respond_to? :setup
|
19
|
+
|
20
|
+
# Determine whether this property name has a group
|
21
|
+
name_parts = name.to_s.split('.', 2)
|
22
|
+
|
23
|
+
# If it has a group, set it
|
24
|
+
if name_parts.length > 1
|
25
|
+
@group = name_parts.first
|
26
|
+
end
|
27
|
+
|
28
|
+
# Set the name
|
29
|
+
@name = name_parts.last.downcase
|
30
|
+
|
31
|
+
# Build out the values/params from the passed arguments
|
32
|
+
valueIdx = 0
|
33
|
+
args.each do |arg|
|
34
|
+
if arg.is_a? Hash
|
35
|
+
arg.each do |param, value|
|
36
|
+
param = param_name(param.to_s.downcase, value)
|
37
|
+
value = param_value(param, value)
|
38
|
+
add_param(param, value)
|
39
|
+
end
|
40
|
+
else
|
41
|
+
value = parse_value(arg.to_s)
|
42
|
+
add_value(value, valueIdx)
|
43
|
+
valueIdx += 1
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.create(vcard, name, *args)
|
49
|
+
name = name.to_s.downcase
|
50
|
+
classname = ''
|
51
|
+
|
52
|
+
case name
|
53
|
+
when 'n'
|
54
|
+
className = 'NameProperty'
|
55
|
+
else
|
56
|
+
className = 'Property'
|
57
|
+
end
|
58
|
+
|
59
|
+
cls = Module.const_get('VCardigan').const_get(className)
|
60
|
+
cls.new(vcard, name, *args)
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.parse(vcard, data)
|
64
|
+
# Gather the parts
|
65
|
+
data = data.strip
|
66
|
+
parts = data.split(':', 2)
|
67
|
+
values = parts.last.split(';')
|
68
|
+
params = parts.first.split(';')
|
69
|
+
name = params.shift
|
70
|
+
|
71
|
+
# Create argument array
|
72
|
+
args = [vcard, name]
|
73
|
+
|
74
|
+
# Add values to array
|
75
|
+
args.concat(values)
|
76
|
+
|
77
|
+
# Add params to array
|
78
|
+
params.each do |param|
|
79
|
+
keyval = param.split('=')
|
80
|
+
hash = Hash[keyval.first, keyval.last]
|
81
|
+
args.push(hash)
|
82
|
+
end
|
83
|
+
|
84
|
+
# Instantiate a new class with the argument array
|
85
|
+
self.create(*args)
|
86
|
+
end
|
87
|
+
|
88
|
+
def value(idx = 0)
|
89
|
+
@values[idx]
|
90
|
+
end
|
91
|
+
|
92
|
+
def param(name)
|
93
|
+
name ? @params[name.to_s.downcase] : nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def to_s
|
97
|
+
# Name/Group
|
98
|
+
name = @name.upcase
|
99
|
+
property = @group ? "#{@group}.#{name}" : name.upcase
|
100
|
+
|
101
|
+
# Params
|
102
|
+
@params.each do |param, value|
|
103
|
+
str = param_to_s(param, value)
|
104
|
+
property << ';' << str if str
|
105
|
+
end
|
106
|
+
|
107
|
+
# Split with colon
|
108
|
+
property << ':'
|
109
|
+
|
110
|
+
# Values
|
111
|
+
@values.each_with_index do |value, idx|
|
112
|
+
property << ';' unless idx === 0
|
113
|
+
property << value
|
114
|
+
end
|
115
|
+
|
116
|
+
line_fold(property)
|
117
|
+
end
|
118
|
+
|
119
|
+
private
|
120
|
+
|
121
|
+
def version
|
122
|
+
@vcard.version
|
123
|
+
end
|
124
|
+
|
125
|
+
def v3?
|
126
|
+
version == '3.0'
|
127
|
+
end
|
128
|
+
|
129
|
+
def v4?
|
130
|
+
version == '4.0'
|
131
|
+
end
|
132
|
+
|
133
|
+
def param_name(name, value)
|
134
|
+
case name
|
135
|
+
when 'type'
|
136
|
+
if value == 'pref'
|
137
|
+
name = 'preferred'
|
138
|
+
end
|
139
|
+
when 'pref'
|
140
|
+
name = 'preferred'
|
141
|
+
end
|
142
|
+
name
|
143
|
+
end
|
144
|
+
|
145
|
+
def param_value(name, value)
|
146
|
+
case name
|
147
|
+
when 'preferred'
|
148
|
+
value = value.to_s
|
149
|
+
number = value.to_i
|
150
|
+
if number > 0
|
151
|
+
value = number
|
152
|
+
else
|
153
|
+
value = nil if value.downcase == 'false' or value == '0'
|
154
|
+
value = 1 if value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
value
|
158
|
+
end
|
159
|
+
|
160
|
+
def parse_value(value)
|
161
|
+
# Parse Apple labels
|
162
|
+
match = value.match(APPLE_LABEL)
|
163
|
+
if match
|
164
|
+
value = match[1]
|
165
|
+
end
|
166
|
+
value
|
167
|
+
end
|
168
|
+
|
169
|
+
def add_value(value, idx)
|
170
|
+
@values.push(value)
|
171
|
+
end
|
172
|
+
|
173
|
+
def add_param(name, value)
|
174
|
+
name = name.to_s.downcase
|
175
|
+
if @params[name]
|
176
|
+
# Create array of param values if we have an existing param name
|
177
|
+
# present in the hash
|
178
|
+
unless @params[name].is_a? Array
|
179
|
+
@params[name] = [@params[name]]
|
180
|
+
end
|
181
|
+
@params[name].push(value)
|
182
|
+
else
|
183
|
+
# Default is to just set the param to the value
|
184
|
+
@params[name] = value
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def param_to_s(name, value)
|
189
|
+
case name
|
190
|
+
when 'preferred'
|
191
|
+
if !value or value === 0
|
192
|
+
return nil
|
193
|
+
end
|
194
|
+
name = v3? ? 'type' : 'pref'
|
195
|
+
value = v3? ? 'pref' : value.is_a?(Numeric) ? value : 1
|
196
|
+
else
|
197
|
+
end
|
198
|
+
value = value.to_s
|
199
|
+
name.upcase << '=' << value unless value.empty?
|
200
|
+
end
|
201
|
+
|
202
|
+
def line_fold(string)
|
203
|
+
chars = @vcard.chars
|
204
|
+
if chars == 0
|
205
|
+
out = string
|
206
|
+
else
|
207
|
+
out = ''
|
208
|
+
while string.length > 0
|
209
|
+
if string.length >= chars
|
210
|
+
amount = out.empty? ? chars : chars - 1
|
211
|
+
out += "#{string.slice!(0, amount)}\n "
|
212
|
+
else
|
213
|
+
out += string.slice!(0, string.length)
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
out
|
218
|
+
end
|
219
|
+
|
220
|
+
end
|
221
|
+
|
222
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
module VCardigan
|
2
|
+
|
3
|
+
class VCard
|
4
|
+
|
5
|
+
VCARD_PATTERN = /BEGIN:VCARD\s+(.*?)VERSION:(.+?)\s+(.+?)END:VCARD/m;
|
6
|
+
|
7
|
+
attr_accessor :version
|
8
|
+
attr_accessor :chars
|
9
|
+
|
10
|
+
def initialize(options = {})
|
11
|
+
# Backwards compatibility
|
12
|
+
if options.is_a? String
|
13
|
+
options = { :version => options }
|
14
|
+
end
|
15
|
+
|
16
|
+
# Default options
|
17
|
+
@version = options[:version] || '4.0'
|
18
|
+
@chars = options[:chars] || 75
|
19
|
+
|
20
|
+
@fields = {}
|
21
|
+
@groups = {}
|
22
|
+
@group = nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def parse(data)
|
26
|
+
match = VCARD_PATTERN.match(data)
|
27
|
+
if match
|
28
|
+
# Set version number
|
29
|
+
@version = match[2]
|
30
|
+
lines = "#{match[1].strip}#{match[3].strip}"
|
31
|
+
|
32
|
+
# Add the parsed properties to this vCard
|
33
|
+
lines.each_line do |line|
|
34
|
+
property = VCardigan::Property.parse(self, line)
|
35
|
+
add_prop(property)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
self
|
39
|
+
end
|
40
|
+
|
41
|
+
def [](group)
|
42
|
+
@group = group
|
43
|
+
self
|
44
|
+
end
|
45
|
+
|
46
|
+
def method_missing(method, *args)
|
47
|
+
if args.empty?
|
48
|
+
# Return the field property/properties when no arguments are passed
|
49
|
+
field(method)
|
50
|
+
else
|
51
|
+
# Add property to vCard
|
52
|
+
add(method, *args)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def add(name, *args)
|
57
|
+
if @group
|
58
|
+
# If there's a group, add it to the name
|
59
|
+
name = "#{@group}.#{name}"
|
60
|
+
|
61
|
+
# Reset group to nil
|
62
|
+
@group = nil
|
63
|
+
end
|
64
|
+
|
65
|
+
# Build the property and add it to the vCard
|
66
|
+
property = build_prop(name, *args)
|
67
|
+
add_prop(property)
|
68
|
+
end
|
69
|
+
|
70
|
+
def field(name)
|
71
|
+
name = name.to_s.downcase
|
72
|
+
if @group and @fields[name]
|
73
|
+
# Finds all items that match the prop type in the group
|
74
|
+
fields = @fields[name].find_all do |prop|
|
75
|
+
prop.group == @group.to_s
|
76
|
+
end
|
77
|
+
|
78
|
+
# Reset the group to nil and return the fields
|
79
|
+
@group = nil
|
80
|
+
fields
|
81
|
+
else
|
82
|
+
@fields[name]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def group(name)
|
87
|
+
@groups[name]
|
88
|
+
end
|
89
|
+
|
90
|
+
def to_s
|
91
|
+
# Raise errors if invalid
|
92
|
+
validate
|
93
|
+
|
94
|
+
# Start vCard
|
95
|
+
vcard = build_prop(:begin, 'VCARD').to_s << "\n"
|
96
|
+
|
97
|
+
# Add version
|
98
|
+
vcard << build_prop(:version, @version).to_s << "\n"
|
99
|
+
|
100
|
+
# Add the properties
|
101
|
+
@fields.each do |field, properties|
|
102
|
+
properties.each do |property|
|
103
|
+
vcard << property.to_s << "\n"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# END
|
108
|
+
vcard << build_prop(:end, 'VCARD').to_s << "\n"
|
109
|
+
|
110
|
+
# Return vCard
|
111
|
+
return vcard
|
112
|
+
end
|
113
|
+
|
114
|
+
# Aliases ##########
|
115
|
+
|
116
|
+
def name(*args)
|
117
|
+
n(*args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def fullname(*args)
|
121
|
+
fn(*args)
|
122
|
+
end
|
123
|
+
|
124
|
+
# Private ##########
|
125
|
+
private
|
126
|
+
|
127
|
+
def build_prop(name, *args)
|
128
|
+
VCardigan::Property.create(self, name, *args)
|
129
|
+
end
|
130
|
+
|
131
|
+
def add_prop(property)
|
132
|
+
name = property.name
|
133
|
+
group = property.group
|
134
|
+
|
135
|
+
# Create a field on the fields hash, if not already present, to house
|
136
|
+
# the property
|
137
|
+
unless @fields.has_key? name
|
138
|
+
@fields[name] = []
|
139
|
+
end
|
140
|
+
|
141
|
+
# Add the property to the field array
|
142
|
+
@fields[name].push(property)
|
143
|
+
|
144
|
+
if group
|
145
|
+
# Add a field on the groups hash, if not already present, to house the
|
146
|
+
# group properties
|
147
|
+
unless @groups.has_key? group
|
148
|
+
@groups[group] = []
|
149
|
+
end
|
150
|
+
|
151
|
+
# Add the property to the groups array
|
152
|
+
@groups[group].push(property)
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def validate
|
157
|
+
unless @fields['fn']
|
158
|
+
raise VCardigan::EncodingError,
|
159
|
+
"vCards must include an FN field"
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
data/lib/vcardigan.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require_relative 'vcardigan/version'
|
2
|
+
require_relative 'vcardigan/vcard'
|
3
|
+
require_relative 'vcardigan/property'
|
4
|
+
require_relative 'vcardigan/properties/name_property'
|
5
|
+
require_relative 'vcardigan/errors'
|
6
|
+
|
7
|
+
module VCardigan
|
8
|
+
|
9
|
+
class << self
|
10
|
+
|
11
|
+
def create(*args)
|
12
|
+
VCardigan::VCard.new(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def parse(*args)
|
16
|
+
VCardigan::VCard.new.parse(*args)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,122 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe VCardigan::Property do
|
4
|
+
|
5
|
+
describe '#init' do
|
6
|
+
let(:vcard) { VCardigan.create }
|
7
|
+
|
8
|
+
context 'without a group' do
|
9
|
+
let(:name) { :email }
|
10
|
+
let(:value) { 'joe@strummer.com' }
|
11
|
+
let(:params) { { :type => 'uri' } }
|
12
|
+
let(:prop) { VCardigan::Property.create(vcard, name, value, params) }
|
13
|
+
|
14
|
+
it 'should set the name' do
|
15
|
+
prop.name.should == name.to_s.downcase
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'should set the value' do
|
19
|
+
prop.values.first.should == value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
describe '#values' do
|
25
|
+
let(:vcard) { VCardigan.create }
|
26
|
+
let(:name) { :email }
|
27
|
+
let(:value) { 'joe@strummer.com' }
|
28
|
+
let(:prop) { VCardigan::Property.create(vcard, name, value) }
|
29
|
+
|
30
|
+
it 'should return the values array' do
|
31
|
+
prop.values.should == prop.instance_variable_get(:@values)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
describe '#value' do
|
36
|
+
let(:vcard) { VCardigan.create }
|
37
|
+
let(:name) { :n }
|
38
|
+
let(:value1) { 'Strummer' }
|
39
|
+
let(:value2) { 'Joe' }
|
40
|
+
let(:prop) { VCardigan::Property.create(vcard, name, value1, value2) }
|
41
|
+
|
42
|
+
context 'without an index' do
|
43
|
+
it 'should return the first item from the values array' do
|
44
|
+
prop.value.should == prop.instance_variable_get(:@values).first
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'with an index' do
|
49
|
+
it 'should return the item corresponding to the index' do
|
50
|
+
prop.value(1).should == prop.instance_variable_get(:@values)[1]
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe '#params' do
|
56
|
+
let(:vcard) { VCardigan.create }
|
57
|
+
let(:name) { :email }
|
58
|
+
let(:value) { 'joe@strummer.com' }
|
59
|
+
let(:params) { { :type => 'uri' } }
|
60
|
+
let(:prop) { VCardigan::Property.create(vcard, name, value, params) }
|
61
|
+
|
62
|
+
it 'should return the params array' do
|
63
|
+
prop.params.should == prop.instance_variable_get(:@params)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe '#param' do
|
68
|
+
let(:vcard) { VCardigan.create }
|
69
|
+
let(:name) { :email }
|
70
|
+
let(:value) { 'joe@strummer.com' }
|
71
|
+
let(:params) { { :type => 'uri' } }
|
72
|
+
let(:prop) { VCardigan::Property.create(vcard, name, value, params) }
|
73
|
+
|
74
|
+
context 'with a param that exists' do
|
75
|
+
it 'should return the param' do
|
76
|
+
prop.param(:type).should == params[:type]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
context 'with a param that does not exist' do
|
81
|
+
it 'should return nil' do
|
82
|
+
prop.param(:random).should == nil
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '#to_s' do
|
88
|
+
let(:vcard) { VCardigan.create }
|
89
|
+
let(:name) { :email }
|
90
|
+
let(:group) { :item1 }
|
91
|
+
let(:value) { 'joe@strummer.com' }
|
92
|
+
let(:params) { { :type => 'uri' } }
|
93
|
+
let(:prop) { VCardigan::Property.create(vcard, "#{group}.#{name}", value, params) }
|
94
|
+
|
95
|
+
it 'should return the property vCard formatted' do
|
96
|
+
prop.to_s.should == "#{group}.#{name.upcase};TYPE=#{params[:type]}:#{value}"
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
describe '#parse' do
|
101
|
+
let(:vcard) { VCardigan.create }
|
102
|
+
let(:name) { :email }
|
103
|
+
let(:group) { :item1 }
|
104
|
+
let(:value) { 'joe@strummer.com' }
|
105
|
+
let(:params) { { :type => 'uri' } }
|
106
|
+
let(:string) { "#{group}.#{name.upcase};TYPE=#{params[:type]}:#{value}" }
|
107
|
+
let(:prop) { VCardigan::Property.parse(vcard, string) }
|
108
|
+
|
109
|
+
it 'should set the group' do
|
110
|
+
prop.group.should == group.to_s.downcase
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should set the name' do
|
114
|
+
prop.name.should == name.to_s.downcase
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'should set the values' do
|
118
|
+
prop.values.should == [value]
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
@@ -0,0 +1,250 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../spec_helper'
|
2
|
+
|
3
|
+
describe VCardigan::VCard do
|
4
|
+
|
5
|
+
describe '#init' do
|
6
|
+
context 'no options' do
|
7
|
+
let(:vcard) { VCardigan.create }
|
8
|
+
|
9
|
+
it 'should set default version' do
|
10
|
+
vcard.version.should == '4.0'
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should set default line char limit' do
|
14
|
+
vcard.chars.should == 75
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
context 'version number for args (backward compat)' do
|
19
|
+
let(:version) { '3.0' }
|
20
|
+
let(:vcard) { VCardigan.create(version) }
|
21
|
+
|
22
|
+
it 'should set version' do
|
23
|
+
vcard.version.should == version
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'passed options args' do
|
28
|
+
let(:version) { '3.0' }
|
29
|
+
let(:chars) { 100 }
|
30
|
+
let(:vcard) { VCardigan.create(:version => version, :chars => chars) }
|
31
|
+
|
32
|
+
it 'should set version' do
|
33
|
+
vcard.version.should == version
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'should set line char limit' do
|
37
|
+
vcard.chars.should == chars
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe '#[]' do
|
43
|
+
let(:group) { :item1 }
|
44
|
+
let(:vcard) { VCardigan.create }
|
45
|
+
|
46
|
+
before do
|
47
|
+
vcard[group]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should return vcard' do
|
51
|
+
vcard.should be_an_instance_of(VCardigan::VCard)
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should set group' do
|
55
|
+
vcard.instance_variable_get(:@group).should == group
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#add' do
|
60
|
+
let(:name) { :email }
|
61
|
+
let(:value) { 'joe@strummer.com' }
|
62
|
+
let(:vcard) { VCardigan.create }
|
63
|
+
let(:fields) { vcard.instance_variable_get(:@fields) }
|
64
|
+
|
65
|
+
context 'with no group' do
|
66
|
+
before do
|
67
|
+
vcard.add(name, value)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should add field name (as string) to fields hash' do
|
71
|
+
fields.should have_key(name.to_s)
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'should create array on field name' do
|
75
|
+
fields[name.to_s].should be_an_instance_of(Array)
|
76
|
+
end
|
77
|
+
|
78
|
+
it 'should have single item in array' do
|
79
|
+
fields[name.to_s].length.should == 1
|
80
|
+
end
|
81
|
+
|
82
|
+
it 'should add property to field name array' do
|
83
|
+
fields[name.to_s].first.should be_an_instance_of(VCardigan::Property)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context 'with group' do
|
88
|
+
let(:group) { :item1 }
|
89
|
+
let(:groups) { vcard.instance_variable_get(:@groups) }
|
90
|
+
|
91
|
+
before do
|
92
|
+
vcard[group].add(name, value)
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should add group name (as string) to groups hash' do
|
96
|
+
groups.should have_key(group.to_s)
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should create array on group name' do
|
100
|
+
groups[group.to_s].should be_an_instance_of(Array)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'should have single item in array' do
|
104
|
+
groups[group.to_s].length.should == 1
|
105
|
+
end
|
106
|
+
|
107
|
+
it 'should add property group name array' do
|
108
|
+
groups[group.to_s].first.should be_an_instance_of(VCardigan::Property)
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe '#method_missing' do
|
114
|
+
let(:vcard) { VCardigan.create }
|
115
|
+
|
116
|
+
context 'with args' do
|
117
|
+
let(:name) { :email }
|
118
|
+
let(:email) { 'joe@strummer.com' }
|
119
|
+
let(:params) { { :type => 'uri' } }
|
120
|
+
|
121
|
+
it 'should call #add' do
|
122
|
+
vcard.should_receive(:add).with(name, email, params)
|
123
|
+
vcard.send(name, email, params)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
context 'without args' do
|
128
|
+
let(:name) { :email }
|
129
|
+
|
130
|
+
it 'should call #field' do
|
131
|
+
vcard.should_receive(:field).with(name)
|
132
|
+
vcard.send(name)
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
describe '#field' do
|
138
|
+
let(:vcard) { VCardigan.create }
|
139
|
+
let(:fields) { vcard.instance_variable_get(:@fields) }
|
140
|
+
let(:name) { :email }
|
141
|
+
let(:value) { 'joe@strummer.com' }
|
142
|
+
|
143
|
+
context 'no group' do
|
144
|
+
before do
|
145
|
+
vcard.send(name, value)
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should return field name array' do
|
149
|
+
vcard.field(name).should == fields[name.to_s]
|
150
|
+
end
|
151
|
+
|
152
|
+
it 'should have correct property in array' do
|
153
|
+
vcard.field(name).first.value.should == value
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
context 'with group' do
|
158
|
+
let(:group) { :item1 }
|
159
|
+
let(:value2) { 'joestrummer@strummer.com' }
|
160
|
+
|
161
|
+
before do
|
162
|
+
vcard.send(name, value)
|
163
|
+
vcard[group].send(name, value2)
|
164
|
+
end
|
165
|
+
|
166
|
+
it 'should return field name array with props within group' do
|
167
|
+
vcard[group].field(name).should == fields[name.to_s].find_all do |prop|
|
168
|
+
prop.group == group.to_s
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
it 'should have correct property in array' do
|
173
|
+
vcard[group].field(name).first.value.should == value2
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe 'Aliases' do
|
179
|
+
let(:vcard) { VCardigan.create }
|
180
|
+
|
181
|
+
context '#name' do
|
182
|
+
it 'should call method_missing with method n' do
|
183
|
+
vcard.should_receive(:method_missing).with(:n)
|
184
|
+
vcard.name
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
context '#fullname' do
|
189
|
+
it 'should call method_missing with method fn' do
|
190
|
+
vcard.should_receive(:method_missing).with(:fn)
|
191
|
+
vcard.fullname
|
192
|
+
end
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe '#to_s' do
|
197
|
+
let(:vcard) { VCardigan.create }
|
198
|
+
|
199
|
+
context 'with no FN' do
|
200
|
+
it 'should raise an error' do
|
201
|
+
expect { vcard.to_s }.to raise_error(VCardigan::EncodingError)
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context 'with an FN and N' do
|
206
|
+
before do
|
207
|
+
vcard.name('Strummer', 'Joe')
|
208
|
+
vcard.fullname('Joe Strummer')
|
209
|
+
end
|
210
|
+
|
211
|
+
it 'should have BEGIN on first line' do
|
212
|
+
vcard.to_s.split("\n").first.should == 'BEGIN:VCARD'
|
213
|
+
end
|
214
|
+
|
215
|
+
it 'should proceed with VERSION' do
|
216
|
+
vcard.to_s.split("\n")[1].should == 'VERSION:4.0'
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should include the N field' do
|
220
|
+
vcard.to_s.split("\n")[2].should == 'N:Strummer;Joe;;;'
|
221
|
+
end
|
222
|
+
|
223
|
+
it 'should include the FN field' do
|
224
|
+
vcard.to_s.split("\n")[3].should == 'FN:Joe Strummer'
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should end with END' do
|
228
|
+
vcard.to_s.split("\n").last.should == 'END:VCARD'
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
describe '#parse' do
|
234
|
+
context 'valid 4.0 vCard' do
|
235
|
+
let(:data) { File.read(File.dirname(__FILE__) + '/../helpers/joe.vcf') }
|
236
|
+
let(:vcard) { VCardigan.parse(data) }
|
237
|
+
let(:fields) { vcard.instance_variable_get(:@fields) }
|
238
|
+
|
239
|
+
it 'should set the version' do
|
240
|
+
vcard.version.should == '4.0'
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should add the properties to the fields array' do
|
244
|
+
fields.should have_key('n')
|
245
|
+
fields.should have_key('fn')
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/../lib/vcardigan'
|
metadata
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: vcardigan
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Morgan
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-01-24 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: rspec
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '2.0'
|
22
|
+
type: :development
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
25
|
+
none: false
|
26
|
+
requirements:
|
27
|
+
- - ~>
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: '2.0'
|
30
|
+
description: vCardigan is a ruby library for building and parsing vCards that supports
|
31
|
+
both v3.0 and v4.0.
|
32
|
+
email: matt@mlmorg.com
|
33
|
+
executables: []
|
34
|
+
extensions: []
|
35
|
+
extra_rdoc_files: []
|
36
|
+
files:
|
37
|
+
- lib/vcardigan/errors.rb
|
38
|
+
- lib/vcardigan/properties/name_property.rb
|
39
|
+
- lib/vcardigan/property.rb
|
40
|
+
- lib/vcardigan/vcard.rb
|
41
|
+
- lib/vcardigan/version.rb
|
42
|
+
- lib/vcardigan.rb
|
43
|
+
- spec/examples/property_spec.rb
|
44
|
+
- spec/examples/vcard_spec.rb
|
45
|
+
- spec/spec_helper.rb
|
46
|
+
homepage: http://github.com/brewster/vcardigan
|
47
|
+
licenses:
|
48
|
+
- MIT
|
49
|
+
post_install_message:
|
50
|
+
rdoc_options: []
|
51
|
+
require_paths:
|
52
|
+
- lib
|
53
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
54
|
+
none: false
|
55
|
+
requirements:
|
56
|
+
- - ! '>='
|
57
|
+
- !ruby/object:Gem::Version
|
58
|
+
version: '0'
|
59
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
60
|
+
none: false
|
61
|
+
requirements:
|
62
|
+
- - ! '>='
|
63
|
+
- !ruby/object:Gem::Version
|
64
|
+
version: '0'
|
65
|
+
requirements: []
|
66
|
+
rubyforge_project:
|
67
|
+
rubygems_version: 1.8.24
|
68
|
+
signing_key:
|
69
|
+
specification_version: 3
|
70
|
+
summary: Ruby vCard Builder/Parser
|
71
|
+
test_files:
|
72
|
+
- spec/examples/property_spec.rb
|
73
|
+
- spec/examples/vcard_spec.rb
|
74
|
+
- spec/spec_helper.rb
|