valerie 0.0.8 → 1.0.0
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.
- checksums.yaml +4 -4
- data/README.md +30 -13
- data/Rakefile +24 -0
- data/lib/valerie/address.rb +47 -6
- data/lib/valerie/card.rb +134 -17
- data/lib/valerie/collection/address_collection.rb +37 -5
- data/lib/valerie/collection/email_collection.rb +22 -2
- data/lib/valerie/collection/phone_collection.rb +21 -2
- data/lib/valerie/core/parser.rb +37 -14
- data/lib/valerie/email.rb +40 -13
- data/lib/valerie/name.rb +40 -6
- data/lib/valerie/phone.rb +45 -1
- data/lib/valerie.rb +43 -7
- data/test/address_test.rb +93 -0
- data/test/card_test.rb +129 -16
- data/test/collection/address_collection_test.rb +97 -0
- data/test/email_test.rb +4 -4
- data/test/phone_test.rb +1 -1
- metadata +19 -3
data/lib/valerie/core/parser.rb
CHANGED
|
@@ -2,7 +2,22 @@ require_relative '../../valerie'
|
|
|
2
2
|
|
|
3
3
|
module Valerie
|
|
4
4
|
module Core
|
|
5
|
+
# Parser module for VCard data
|
|
6
|
+
# @api private
|
|
5
7
|
module Parser
|
|
8
|
+
# Parse VCard data from string or array format
|
|
9
|
+
#
|
|
10
|
+
# @param data [String, Array<String>] VCard data to parse
|
|
11
|
+
# @return [Array<Card>, Card] Parsed card(s)
|
|
12
|
+
# - Returns Array<Card> when parsing from string
|
|
13
|
+
# - Returns single Card when parsing from array
|
|
14
|
+
# @raise [ArgumentError] if data is neither String nor Array
|
|
15
|
+
# @note The return type inconsistency is a known issue
|
|
16
|
+
# @example Parse from string
|
|
17
|
+
# cards = Valerie::Card.parse("BEGIN:VCARD\r\n...")
|
|
18
|
+
# card = cards.first
|
|
19
|
+
# @example Parse from array
|
|
20
|
+
# card = Valerie::Card.parse(['N:Doe;John', 'TEL;:555-1234'])
|
|
6
21
|
def parse(data)
|
|
7
22
|
if data.instance_of?(String)
|
|
8
23
|
from_s(data)
|
|
@@ -12,40 +27,48 @@ module Valerie
|
|
|
12
27
|
raise ArgumentError, "Expected String or Array, got #{data.class}"
|
|
13
28
|
end
|
|
14
29
|
end
|
|
15
|
-
|
|
30
|
+
|
|
16
31
|
private
|
|
17
32
|
def from_a(data)
|
|
18
33
|
Card.new.tap do |vcard|
|
|
19
34
|
if (name = data.find { _1.start_with?("N:") })
|
|
20
35
|
vcard.name = Name.from_s name.split(":").last
|
|
21
36
|
end
|
|
22
|
-
|
|
37
|
+
|
|
38
|
+
if (formatted_name = data.find { _1.start_with?("FN:") })
|
|
39
|
+
vcard.instance_variable_set(:@formatted_name, formatted_name.split(":", 2).last)
|
|
40
|
+
end
|
|
41
|
+
|
|
23
42
|
if (organization = data.find { _1.start_with?("ORG:") })
|
|
24
43
|
vcard.organization = Organization.from_s(organization)
|
|
25
44
|
end
|
|
26
|
-
|
|
45
|
+
|
|
27
46
|
if (birthday = data.find { _1.start_with?("BDAY:") })
|
|
28
47
|
vcard.birthday = Birthday.from_s(birthday)
|
|
29
48
|
end
|
|
30
|
-
|
|
49
|
+
|
|
31
50
|
if (gender = data.find { _1.start_with?("GENDER:") })
|
|
32
51
|
vcard.gender = gender.split(":").last
|
|
33
52
|
end
|
|
34
|
-
|
|
53
|
+
|
|
35
54
|
data.select { _1.include?("TEL;") }.each do |phone|
|
|
36
55
|
vcard.phones.add(Phone.from_s(phone))
|
|
37
56
|
end
|
|
38
|
-
|
|
57
|
+
|
|
39
58
|
data.select { _1.start_with?("EMAIL;") }.each do |email|
|
|
40
59
|
vcard.emails.add(Email.from_s(email))
|
|
41
60
|
end
|
|
61
|
+
|
|
62
|
+
data.select { _1.start_with?("ADR;") }.each do |address|
|
|
63
|
+
vcard.addresses.add(Address.from_s(address))
|
|
64
|
+
end
|
|
42
65
|
end
|
|
43
66
|
end
|
|
44
|
-
|
|
67
|
+
|
|
45
68
|
def from_s(str)
|
|
46
69
|
vcards = []
|
|
47
70
|
vcard = []
|
|
48
|
-
|
|
71
|
+
|
|
49
72
|
unfold(str).each do |entry|
|
|
50
73
|
if entry.include?("VERSION:")
|
|
51
74
|
vcards << from_a(vcard) unless vcard.empty?
|
|
@@ -54,17 +77,17 @@ module Valerie
|
|
|
54
77
|
vcard << entry
|
|
55
78
|
end
|
|
56
79
|
end
|
|
57
|
-
|
|
80
|
+
|
|
58
81
|
vcards << from_a(vcard)
|
|
59
|
-
|
|
82
|
+
|
|
60
83
|
vcards
|
|
61
84
|
end
|
|
62
|
-
|
|
85
|
+
|
|
63
86
|
UNTERMINATED_QUOTED_PRINTABLE = /ENCODING=QUOTED-PRINTABLE:.*=$/
|
|
64
|
-
|
|
87
|
+
|
|
65
88
|
def unfold(vcard)
|
|
66
89
|
unfolded = []
|
|
67
|
-
|
|
90
|
+
|
|
68
91
|
prior_line = nil
|
|
69
92
|
vcard.lines do |line|
|
|
70
93
|
line.chomp!
|
|
@@ -82,7 +105,7 @@ module Valerie
|
|
|
82
105
|
end
|
|
83
106
|
prior_line = unfolded[-1]
|
|
84
107
|
end
|
|
85
|
-
|
|
108
|
+
|
|
86
109
|
unfolded
|
|
87
110
|
end
|
|
88
111
|
end
|
data/lib/valerie/email.rb
CHANGED
|
@@ -1,42 +1,69 @@
|
|
|
1
1
|
require_relative 'ordered'
|
|
2
2
|
|
|
3
3
|
module Valerie
|
|
4
|
+
# Represents an email address in a VCard
|
|
5
|
+
#
|
|
6
|
+
# @example Simple email
|
|
7
|
+
# email = Valerie::Email.new(address: 'user@example.com')
|
|
8
|
+
#
|
|
9
|
+
# @example Email with type
|
|
10
|
+
# email = Valerie::Email.new(address: 'work@example.com', type: :work)
|
|
11
|
+
#
|
|
12
|
+
# @example Email with multiple types
|
|
13
|
+
# email = Valerie::Email.new(address: 'user@example.com', type: [:work, :internet])
|
|
4
14
|
class Email
|
|
5
15
|
include Ordered
|
|
6
|
-
|
|
16
|
+
|
|
17
|
+
# Parse an email from VCard EMAIL field string
|
|
18
|
+
#
|
|
19
|
+
# @param data [String] EMAIL field string (e.g., "EMAIL;TYPE=work:user@example.com")
|
|
20
|
+
# @return [Email] Parsed email object
|
|
21
|
+
# @api private
|
|
7
22
|
def self.from_s(data)
|
|
8
23
|
data = data[data.index("EMAIL;")..] unless data.start_with?("EMAIL;")
|
|
9
24
|
identifier = data.split(":").last.split(";")
|
|
10
25
|
options = data.gsub("EMAIL", "").split(":").first.split(";").compact.filter { _1.to_s.include?('=')}.map { _1.downcase.split("=") }.to_h
|
|
11
|
-
|
|
26
|
+
|
|
12
27
|
new(
|
|
13
28
|
address: identifier[0],
|
|
14
29
|
**options
|
|
15
30
|
)
|
|
16
31
|
end
|
|
17
|
-
|
|
32
|
+
|
|
33
|
+
# @return [String] The email address
|
|
34
|
+
# @return [Hash] Additional options (type, position, etc.)
|
|
18
35
|
attr_reader :address, :options
|
|
19
|
-
|
|
36
|
+
|
|
37
|
+
# Create a new email address
|
|
38
|
+
#
|
|
39
|
+
# @param address [String] The email address
|
|
40
|
+
# @param options [Hash] Additional options
|
|
41
|
+
# @option options [String, Symbol, Array] :type Type(s) like :work, :home, :internet
|
|
42
|
+
# @option options [Integer] :position Position in collection (1-based, used internally)
|
|
43
|
+
# @raise [ArgumentError] if position is invalid (< 1)
|
|
44
|
+
# @return [Email]
|
|
45
|
+
# @example
|
|
46
|
+
# email = Valerie::Email.new(address: 'user@example.com', type: :work)
|
|
20
47
|
def initialize(address:, **options)
|
|
21
48
|
@address = address
|
|
22
49
|
@options = options
|
|
23
|
-
|
|
50
|
+
|
|
24
51
|
raise ArgumentError, 'Invalid Position' if invalid_position?
|
|
25
52
|
end
|
|
26
|
-
|
|
53
|
+
|
|
27
54
|
def [](key)
|
|
28
55
|
@options[key]
|
|
29
56
|
end
|
|
30
|
-
|
|
57
|
+
|
|
31
58
|
def to_s
|
|
32
59
|
parts = ['EMAIL']
|
|
33
|
-
|
|
34
|
-
parts << "
|
|
60
|
+
|
|
61
|
+
parts << "PREF=#{@options[:position]}" if position?
|
|
35
62
|
parts << type_to_s if type?
|
|
36
|
-
|
|
63
|
+
|
|
37
64
|
parts.join(';') + ":#{@address}"
|
|
38
65
|
end
|
|
39
|
-
|
|
66
|
+
|
|
40
67
|
def to_h
|
|
41
68
|
{
|
|
42
69
|
address: @address,
|
|
@@ -44,12 +71,12 @@ module Valerie
|
|
|
44
71
|
type: @options[:type],
|
|
45
72
|
}
|
|
46
73
|
end
|
|
47
|
-
|
|
74
|
+
|
|
48
75
|
private
|
|
49
76
|
def type?
|
|
50
77
|
@options[:type]
|
|
51
78
|
end
|
|
52
|
-
|
|
79
|
+
|
|
53
80
|
def type_to_s
|
|
54
81
|
if @options[:type].is_a?(Array)
|
|
55
82
|
"TYPE=\"#{@options[:type].join(',')}\""
|
data/lib/valerie/name.rb
CHANGED
|
@@ -1,24 +1,58 @@
|
|
|
1
1
|
module Valerie
|
|
2
|
+
# Represents a person's name in a VCard
|
|
3
|
+
#
|
|
4
|
+
# @example With hash
|
|
5
|
+
# name = Valerie::Name.new(first_name: 'John', last_name: 'Doe')
|
|
6
|
+
#
|
|
7
|
+
# @example With full details
|
|
8
|
+
# name = Valerie::Name.new(
|
|
9
|
+
# first_name: 'John',
|
|
10
|
+
# last_name: 'Doe',
|
|
11
|
+
# middle_name: 'Michael',
|
|
12
|
+
# prefix: 'Dr.',
|
|
13
|
+
# suffix: 'Jr.'
|
|
14
|
+
# )
|
|
15
|
+
#
|
|
16
|
+
# @example With string (splits on first space)
|
|
17
|
+
# name = Valerie::Name.new('John Doe')
|
|
2
18
|
class Name
|
|
3
19
|
class << self
|
|
20
|
+
# Create a new Name from various input formats
|
|
21
|
+
#
|
|
22
|
+
# @param parts [Hash, String] Name data
|
|
23
|
+
# @return [Name]
|
|
4
24
|
def new(parts)
|
|
5
25
|
if parts.is_a?(Hash)
|
|
6
26
|
super **parts
|
|
7
27
|
else
|
|
8
28
|
first_name, last_name = parts.split(" ")
|
|
9
|
-
|
|
29
|
+
|
|
10
30
|
super first_name:, last_name:
|
|
11
31
|
end
|
|
12
32
|
end
|
|
13
|
-
|
|
33
|
+
|
|
34
|
+
# Parse a name from VCard N field string
|
|
35
|
+
#
|
|
36
|
+
# @param data [String] N field string
|
|
37
|
+
# @return [Name] Parsed name object
|
|
38
|
+
# @api private
|
|
14
39
|
def from_s(data)
|
|
15
40
|
parts = data.gsub("N:", "").split(";")
|
|
16
41
|
new first_name: parts[1], last_name: parts[0], middle_name: parts[2], prefix: parts[3], suffix: parts[4]
|
|
17
42
|
end
|
|
18
43
|
end
|
|
19
|
-
|
|
44
|
+
|
|
45
|
+
# @return [String, nil] Given name (first name)
|
|
46
|
+
# @return [String, nil] Family name (last name)
|
|
20
47
|
attr_reader :first_name, :last_name
|
|
21
|
-
|
|
48
|
+
|
|
49
|
+
# Create a new name
|
|
50
|
+
#
|
|
51
|
+
# @param first_name [String, nil] Given name
|
|
52
|
+
# @param last_name [String, nil] Family name
|
|
53
|
+
# @param middle_name [String, nil] Middle name
|
|
54
|
+
# @param prefix [String, nil] Name prefix (e.g., "Dr.", "Ms.")
|
|
55
|
+
# @param suffix [String, nil] Name suffix (e.g., "Jr.", "PhD")
|
|
22
56
|
def initialize(first_name: nil, last_name: nil, middle_name: nil, prefix: nil, suffix: nil)
|
|
23
57
|
@first_name = first_name
|
|
24
58
|
@last_name = last_name
|
|
@@ -26,11 +60,11 @@ module Valerie
|
|
|
26
60
|
@prefix = prefix
|
|
27
61
|
@suffix = suffix
|
|
28
62
|
end
|
|
29
|
-
|
|
63
|
+
|
|
30
64
|
def to_s
|
|
31
65
|
"N:#{[@last_name, @first_name, @middle_name, @prefix, @suffix].join(";")}"
|
|
32
66
|
end
|
|
33
|
-
|
|
67
|
+
|
|
34
68
|
def to_h
|
|
35
69
|
{
|
|
36
70
|
first_name: @first_name,
|
data/lib/valerie/phone.rb
CHANGED
|
@@ -1,11 +1,31 @@
|
|
|
1
1
|
require_relative 'ordered'
|
|
2
2
|
|
|
3
3
|
module Valerie
|
|
4
|
+
# Represents a phone number in a VCard
|
|
5
|
+
#
|
|
6
|
+
# @example Simple phone number
|
|
7
|
+
# phone = Valerie::Phone.new('+1-555-1234')
|
|
8
|
+
#
|
|
9
|
+
# @example Phone with type
|
|
10
|
+
# phone = Valerie::Phone.new('+1-555-1234', type: :work)
|
|
11
|
+
#
|
|
12
|
+
# @example Phone with multiple types
|
|
13
|
+
# phone = Valerie::Phone.new('+1-555-1234', type: [:work, :voice])
|
|
14
|
+
#
|
|
15
|
+
# @example Phone with position
|
|
16
|
+
# phone = Valerie::Phone.new('+1-555-1234', type: :cell, position: 1)
|
|
4
17
|
class Phone
|
|
18
|
+
# Valid phone type values according to VCard spec
|
|
19
|
+
# @note This constant is currently not enforced
|
|
5
20
|
VALID_TYPES = %w[text voice fax cell video pager textphone].freeze
|
|
6
21
|
|
|
7
22
|
include Ordered
|
|
8
23
|
|
|
24
|
+
# Parse a phone number from VCard TEL field string
|
|
25
|
+
#
|
|
26
|
+
# @param data [String] TEL field string (e.g., "TEL;TYPE=work:+1-555-1234")
|
|
27
|
+
# @return [Phone] Parsed phone object
|
|
28
|
+
# @api private
|
|
9
29
|
def self.from_s(data)
|
|
10
30
|
data = data[data.index("TEL;")..] unless data.start_with?("TEL;")
|
|
11
31
|
identifier = data.split(":").last
|
|
@@ -14,8 +34,20 @@ module Valerie
|
|
|
14
34
|
new(identifier, **options)
|
|
15
35
|
end
|
|
16
36
|
|
|
37
|
+
# @return [String] The phone number
|
|
38
|
+
# @return [Hash] Additional options (type, position, etc.)
|
|
17
39
|
attr_reader :number, :options
|
|
18
40
|
|
|
41
|
+
# Create a new phone number
|
|
42
|
+
#
|
|
43
|
+
# @param number [String] The phone number
|
|
44
|
+
# @param options [Hash] Additional options
|
|
45
|
+
# @option options [String, Symbol, Array] :type Type(s) like :work, :home, :cell, :voice, :fax
|
|
46
|
+
# @option options [Integer] :position Position in collection (1-based, used internally)
|
|
47
|
+
# @raise [ArgumentError] if position is invalid (< 1)
|
|
48
|
+
# @return [Phone]
|
|
49
|
+
# @example
|
|
50
|
+
# phone = Valerie::Phone.new('+1-555-1234', type: :work)
|
|
19
51
|
def initialize(number, **options)
|
|
20
52
|
@number = number
|
|
21
53
|
@options = options.transform_keys!(&:to_sym)
|
|
@@ -24,19 +56,31 @@ module Valerie
|
|
|
24
56
|
raise ArgumentError, 'Invalid Position' if invalid_position?
|
|
25
57
|
end
|
|
26
58
|
|
|
59
|
+
# Access phone options
|
|
60
|
+
#
|
|
61
|
+
# @param key [Symbol] Option key
|
|
62
|
+
# @return [Object, nil] Option value
|
|
27
63
|
def [](key)
|
|
28
64
|
@options[key]
|
|
29
65
|
end
|
|
30
66
|
|
|
67
|
+
# Convert to VCard TEL field string
|
|
68
|
+
#
|
|
69
|
+
# @return [String] TEL field in VCard format
|
|
70
|
+
# @example
|
|
71
|
+
# phone.to_s #=> "TEL;PREF=1;TYPE=work:+1-555-1234"
|
|
31
72
|
def to_s
|
|
32
73
|
parts = ['TEL']
|
|
33
74
|
|
|
34
|
-
parts << "
|
|
75
|
+
parts << "PREF=#{@options[:position]}" if position?
|
|
35
76
|
parts << type_to_s if type?
|
|
36
77
|
|
|
37
78
|
parts.join(';') + ":#{@number}"
|
|
38
79
|
end
|
|
39
80
|
|
|
81
|
+
# Convert to hash representation
|
|
82
|
+
#
|
|
83
|
+
# @return [Hash] Hash with :number, :type, and :position keys
|
|
40
84
|
def to_h
|
|
41
85
|
{
|
|
42
86
|
number: @number,
|
data/lib/valerie.rb
CHANGED
|
@@ -11,28 +11,64 @@ require 'valerie/collection/email_collection'
|
|
|
11
11
|
require 'valerie/collection/address_collection'
|
|
12
12
|
require 'valerie/collection/phone_collection'
|
|
13
13
|
|
|
14
|
+
# Valerie is a VCard 3.0 parser and generator for Ruby.
|
|
15
|
+
# It provides a simple and flexible API for creating, parsing, and managing contact cards.
|
|
16
|
+
#
|
|
17
|
+
# @example Basic usage
|
|
18
|
+
# card = Valerie::Card.new
|
|
19
|
+
# card.name = { first_name: 'John', last_name: 'Doe' }
|
|
20
|
+
# card.emails.add('john@example.com', type: 'work')
|
|
21
|
+
# puts card.to_s
|
|
22
|
+
#
|
|
23
|
+
# @example Parsing a VCard
|
|
24
|
+
# vcard_string = "BEGIN:VCARD\r\nVERSION:3.0\r\n..."
|
|
25
|
+
# cards = Valerie::Card.parse(vcard_string)
|
|
26
|
+
#
|
|
27
|
+
# @see https://github.com/hellotext/valerie
|
|
14
28
|
module Valerie
|
|
15
|
-
|
|
16
|
-
|
|
29
|
+
# Current version of the Valerie gem
|
|
30
|
+
VERSION = '1.0.0'.freeze
|
|
31
|
+
|
|
32
|
+
# Get the global configuration object
|
|
33
|
+
#
|
|
34
|
+
# @return [Configuration] The global configuration
|
|
17
35
|
def self.configuration
|
|
18
36
|
@configuration ||= Configuration.new
|
|
19
37
|
end
|
|
20
|
-
|
|
38
|
+
|
|
39
|
+
# Configure Valerie globally
|
|
40
|
+
#
|
|
41
|
+
# @yield [Configuration] The configuration object
|
|
42
|
+
# @example
|
|
43
|
+
# Valerie.configure do |config|
|
|
44
|
+
# config.product = 'My App'
|
|
45
|
+
# config.version = '4.0'
|
|
46
|
+
# end
|
|
21
47
|
def self.configure
|
|
22
48
|
yield(configuration)
|
|
23
49
|
end
|
|
24
|
-
|
|
50
|
+
|
|
51
|
+
# Global configuration for VCard generation
|
|
25
52
|
class Configuration
|
|
53
|
+
# @return [String] Product identifier for PRODID field
|
|
54
|
+
# @return [String] VCard version number
|
|
55
|
+
# @return [String] Language code for the card
|
|
26
56
|
attr_accessor :product, :version, :language
|
|
27
|
-
|
|
57
|
+
|
|
58
|
+
# Get the product identifier (defaults to 'Valerie www.hellotext.com')
|
|
59
|
+
# @return [String]
|
|
28
60
|
def product
|
|
29
61
|
@product ||= 'Valerie www.hellotext.com'
|
|
30
62
|
end
|
|
31
|
-
|
|
63
|
+
|
|
64
|
+
# Get the VCard version (defaults to '3.0')
|
|
65
|
+
# @return [String]
|
|
32
66
|
def version
|
|
33
67
|
@version ||= '3.0'
|
|
34
68
|
end
|
|
35
|
-
|
|
69
|
+
|
|
70
|
+
# Get the language code (defaults to 'EN')
|
|
71
|
+
# @return [String]
|
|
36
72
|
def language
|
|
37
73
|
@language ||= 'EN'
|
|
38
74
|
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require_relative 'test_helper'
|
|
2
|
+
|
|
3
|
+
class AddressTest < Minitest::Test
|
|
4
|
+
def test_initialize_with_all_fields
|
|
5
|
+
address = Valerie::Address.new(
|
|
6
|
+
post_office_box: 'PO Box 123',
|
|
7
|
+
extended_address: 'Suite 200',
|
|
8
|
+
street_address: '123 Main St',
|
|
9
|
+
locality: 'New York',
|
|
10
|
+
region: 'NY',
|
|
11
|
+
postal_code: '10001',
|
|
12
|
+
country: 'USA'
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
assert_equal(address.to_h[:post_office_box], 'PO Box 123')
|
|
16
|
+
assert_equal(address.to_h[:extended_address], 'Suite 200')
|
|
17
|
+
assert_equal(address.to_h[:street_address], '123 Main St')
|
|
18
|
+
assert_equal(address.to_h[:locality], 'New York')
|
|
19
|
+
assert_equal(address.to_h[:region], 'NY')
|
|
20
|
+
assert_equal(address.to_h[:postal_code], '10001')
|
|
21
|
+
assert_equal(address.to_h[:country], 'USA')
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def test_to_s_format
|
|
25
|
+
address = Valerie::Address.new(
|
|
26
|
+
post_office_box: '',
|
|
27
|
+
extended_address: '',
|
|
28
|
+
street_address: '123 Main St',
|
|
29
|
+
locality: 'New York',
|
|
30
|
+
region: 'NY',
|
|
31
|
+
postal_code: '10001',
|
|
32
|
+
country: 'USA'
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
assert_equal(address.to_s.start_with?('ADR:'), true)
|
|
36
|
+
assert_equal(address.identifier, ';;123 Main St;New York;NY;10001;USA')
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def test_to_s_with_position
|
|
40
|
+
address = Valerie::Address.new(
|
|
41
|
+
post_office_box: '',
|
|
42
|
+
extended_address: '',
|
|
43
|
+
street_address: '123 Main St',
|
|
44
|
+
locality: 'New York',
|
|
45
|
+
region: 'NY',
|
|
46
|
+
postal_code: '10001',
|
|
47
|
+
country: 'USA',
|
|
48
|
+
position: 1
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
assert_equal(address.to_s, 'ADR;PREF=1:;;123 Main St;New York;NY;10001;USA')
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def test_with_invalid_position
|
|
55
|
+
error = assert_raises(ArgumentError) do
|
|
56
|
+
Valerie::Address.new(
|
|
57
|
+
post_office_box: '',
|
|
58
|
+
extended_address: '',
|
|
59
|
+
street_address: '123 Main St',
|
|
60
|
+
locality: 'New York',
|
|
61
|
+
region: 'NY',
|
|
62
|
+
postal_code: '10001',
|
|
63
|
+
country: 'USA',
|
|
64
|
+
position: -1
|
|
65
|
+
)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
assert_equal(error.message, 'Invalid Position')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def test_from_s_parsing
|
|
72
|
+
address_string = 'ADR;TYPE=work:PO Box 123;Suite 200;123 Main St;New York;NY;10001;USA'
|
|
73
|
+
address = Valerie::Address.from_s(address_string)
|
|
74
|
+
|
|
75
|
+
assert_equal(address.to_h[:post_office_box], 'PO Box 123')
|
|
76
|
+
assert_equal(address.to_h[:extended_address], 'Suite 200')
|
|
77
|
+
assert_equal(address.to_h[:street_address], '123 Main St')
|
|
78
|
+
assert_equal(address.to_h[:locality], 'New York')
|
|
79
|
+
assert_equal(address.to_h[:region], 'NY')
|
|
80
|
+
assert_equal(address.to_h[:postal_code], '10001')
|
|
81
|
+
assert_equal(address.to_h[:country], 'USA')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def test_from_s_parsing_with_empty_fields
|
|
85
|
+
address_string = 'ADR;TYPE=home:;;456 Oak Ave;Los Angeles;CA;90001;USA'
|
|
86
|
+
address = Valerie::Address.from_s(address_string)
|
|
87
|
+
|
|
88
|
+
assert_equal(address.to_h[:post_office_box], '')
|
|
89
|
+
assert_equal(address.to_h[:extended_address], '')
|
|
90
|
+
assert_equal(address.to_h[:street_address], '456 Oak Ave')
|
|
91
|
+
assert_equal(address.to_h[:locality], 'Los Angeles')
|
|
92
|
+
end
|
|
93
|
+
end
|