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
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b91f4c4cf72b67b12a9d9b801981c087a70fd9fd999e2c86b864f58ef002c074
|
|
4
|
+
data.tar.gz: a20aa5ec46fda11f4ee95b1f560af1a7c9468ea0773f465a00cbb6a64e617d6e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 10a5580d8e3239ab5a3a1690dd00844d22e08fa8549758cfbf1711bf0ffc40cef476531a0aacea6978b0bffe89e374b70313ff459636b48be53a3476743c58e0
|
|
7
|
+
data.tar.gz: 8f332e9666ea5fbcf51f0c0984c5571e6d81f7782925e40481b91e41ed240588004e5b3307757375de8cafcd7e8c3453662ddabc60785487a4e7759b2f9c352d
|
data/README.md
CHANGED
|
@@ -11,7 +11,7 @@ Add this line to your application's Gemfile:
|
|
|
11
11
|
gem 'valerie'
|
|
12
12
|
```
|
|
13
13
|
|
|
14
|
-
Or install it yourself as:
|
|
14
|
+
Or install it yourself as:
|
|
15
15
|
|
|
16
16
|
```bash
|
|
17
17
|
gem install valerie
|
|
@@ -19,7 +19,7 @@ gem install valerie
|
|
|
19
19
|
|
|
20
20
|
## Usage
|
|
21
21
|
|
|
22
|
-
Parsing a VCard is as simple as passing a VCard string, like
|
|
22
|
+
Parsing a VCard is as simple as passing a VCard string, like
|
|
23
23
|
|
|
24
24
|
```ruby
|
|
25
25
|
data = "BEGIN:VCARD\r\nVERSION:3.0\r\nPRODID:-//Hellotext www.hellotext.com//EN\r\nN:Rosenbaum;Shira;;;\r\nTEL;:+598 00 000 00\r\nEND:VCARD"
|
|
@@ -42,8 +42,7 @@ According to the VCard 3.0 specification, some types of properties can have mult
|
|
|
42
42
|
- `TEL`: for telephone numbers
|
|
43
43
|
- `ADR`: for addresses
|
|
44
44
|
|
|
45
|
-
Aside from these, every other support property supports a single value.
|
|
46
|
-
|
|
45
|
+
Aside from these, every other support property supports a single value.
|
|
47
46
|
|
|
48
47
|
### Single value properties
|
|
49
48
|
|
|
@@ -69,9 +68,9 @@ Or an array with the following order `[first_name, last_name, middle_name, prefi
|
|
|
69
68
|
card.name = %w[Shira Rosenbaum M Ms. PhD]
|
|
70
69
|
```
|
|
71
70
|
|
|
72
|
-
#### Gender
|
|
71
|
+
#### Gender
|
|
73
72
|
|
|
74
|
-
To set the Gender of the card you can, set it value `Card#gender=`, this accepts one of the following constants:
|
|
73
|
+
To set the Gender of the card you can, set it value `Card#gender=`, this accepts one of the following constants:
|
|
75
74
|
`male`, `female`, `other`, `none` and `unknown`. Passing another value raises an `ArgumentError`.
|
|
76
75
|
|
|
77
76
|
```ruby
|
|
@@ -97,7 +96,7 @@ card.organization = { organization: 'Hellotext', department: 'Engineering' }
|
|
|
97
96
|
card.organization = 'Hellotext'
|
|
98
97
|
```
|
|
99
98
|
|
|
100
|
-
### Collections
|
|
99
|
+
### Collections
|
|
101
100
|
|
|
102
101
|
The card exposes methods to adding the respective collectable properties. Email, telephone and address.
|
|
103
102
|
|
|
@@ -129,11 +128,11 @@ card.addresses.add(
|
|
|
129
128
|
)
|
|
130
129
|
```
|
|
131
130
|
|
|
132
|
-
#### Positions
|
|
131
|
+
#### Positions
|
|
133
132
|
|
|
134
|
-
Emails, phones and addresses are ordered. They can include an optional `position` argument
|
|
133
|
+
Emails, phones and addresses are ordered. They can include an optional `position` argument
|
|
135
134
|
to specify their order when the profile has multiple values. Whenever you add a new value,
|
|
136
|
-
a default position argument is picked and the value is appended as the last element.
|
|
135
|
+
a default position argument is picked and the value is appended as the last element.
|
|
137
136
|
|
|
138
137
|
To specify the position, you can pass the `position` argument as a keyword argument. Unlike arrays
|
|
139
138
|
the position is 1-based and not 0-based.
|
|
@@ -153,9 +152,9 @@ card.emails.add('user@domain.com', type: 'work')
|
|
|
153
152
|
card.emails.add('user@domain.com', type: %w[work internet])
|
|
154
153
|
```
|
|
155
154
|
|
|
156
|
-
### Configuration
|
|
155
|
+
### Configuration
|
|
157
156
|
|
|
158
|
-
You can configure the following properties of the card.
|
|
157
|
+
You can configure the following properties of the card.
|
|
159
158
|
|
|
160
159
|
- `prodid`: The product id of the card. This defaults to 'Valerie www.hellotext.com'.
|
|
161
160
|
- `version`: The version of the card. This defaults to '3.0'.
|
|
@@ -169,6 +168,24 @@ Valerie.configure do |config|
|
|
|
169
168
|
end
|
|
170
169
|
```
|
|
171
170
|
|
|
171
|
+
### Documentation
|
|
172
|
+
|
|
173
|
+
Valerie uses [YARD](https://yardoc.org/) for API documentation. To generate the documentation locally:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
# Install dependencies
|
|
177
|
+
bundle install
|
|
178
|
+
|
|
179
|
+
# Generate documentation
|
|
180
|
+
bundle exec rake yard
|
|
181
|
+
|
|
182
|
+
# Or generate and view
|
|
183
|
+
bundle exec rake doc
|
|
184
|
+
open doc/index.html
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The documentation covers all public APIs with examples and usage information.
|
|
188
|
+
|
|
172
189
|
### Licence
|
|
173
190
|
|
|
174
191
|
This code is released under the MIT License. See the LICENSE file for more information.
|
|
@@ -181,7 +198,7 @@ This code is released under the MIT License. See the LICENSE file for more infor
|
|
|
181
198
|
|
|
182
199
|
Contributions are welcome. Please follow the steps below to contribute.
|
|
183
200
|
|
|
184
|
-
1. Fork it
|
|
201
|
+
1. Fork it
|
|
185
202
|
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
186
203
|
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
187
204
|
4. Push to the branch (`git push origin my-new-feature`)
|
data/Rakefile
CHANGED
|
@@ -6,3 +6,27 @@ end
|
|
|
6
6
|
|
|
7
7
|
desc 'Run tests'
|
|
8
8
|
task default: :test
|
|
9
|
+
|
|
10
|
+
# YARD documentation task
|
|
11
|
+
begin
|
|
12
|
+
require 'yard'
|
|
13
|
+
|
|
14
|
+
YARD::Rake::YardocTask.new do |t|
|
|
15
|
+
t.files = ['lib/**/*.rb']
|
|
16
|
+
t.options = ['--markup', 'markdown', '--title', 'Valerie Documentation']
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc 'Generate YARD documentation and open in browser'
|
|
20
|
+
task :doc => :yard do
|
|
21
|
+
puts "Documentation generated in doc/"
|
|
22
|
+
puts "Run 'open doc/index.html' to view"
|
|
23
|
+
end
|
|
24
|
+
rescue LoadError
|
|
25
|
+
# YARD not available
|
|
26
|
+
desc 'Generate YARD documentation (YARD not installed)'
|
|
27
|
+
task :yard do
|
|
28
|
+
puts "YARD is not available. Install it with: gem install yard"
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
task :doc => :yard
|
|
32
|
+
end
|
data/lib/valerie/address.rb
CHANGED
|
@@ -1,9 +1,37 @@
|
|
|
1
1
|
require_relative 'ordered'
|
|
2
2
|
|
|
3
3
|
module Valerie
|
|
4
|
+
# Represents a physical address in a VCard
|
|
5
|
+
#
|
|
6
|
+
# @example Full address
|
|
7
|
+
# address = Valerie::Address.new(
|
|
8
|
+
# post_office_box: 'PO Box 123',
|
|
9
|
+
# extended_address: 'Suite 200',
|
|
10
|
+
# street_address: '123 Main St',
|
|
11
|
+
# locality: 'New York',
|
|
12
|
+
# region: 'NY',
|
|
13
|
+
# postal_code: '10001',
|
|
14
|
+
# country: 'USA'
|
|
15
|
+
# )
|
|
16
|
+
#
|
|
17
|
+
# @example Simple address
|
|
18
|
+
# address = Valerie::Address.new(
|
|
19
|
+
# post_office_box: '',
|
|
20
|
+
# extended_address: '',
|
|
21
|
+
# street_address: '123 Main St',
|
|
22
|
+
# locality: 'New York',
|
|
23
|
+
# region: 'NY',
|
|
24
|
+
# postal_code: '10001',
|
|
25
|
+
# country: 'USA'
|
|
26
|
+
# )
|
|
4
27
|
class Address
|
|
5
28
|
include Ordered
|
|
6
|
-
|
|
29
|
+
|
|
30
|
+
# Parse an address from VCard ADR field string
|
|
31
|
+
#
|
|
32
|
+
# @param data [String] ADR field string
|
|
33
|
+
# @return [Address] Parsed address object
|
|
34
|
+
# @api private
|
|
7
35
|
def self.from_s(data)
|
|
8
36
|
data = data[data.index("ADR;")..] unless data.start_with?("ADR;")
|
|
9
37
|
identifier = data.split(":").last.split(";")
|
|
@@ -21,6 +49,19 @@ module Valerie
|
|
|
21
49
|
)
|
|
22
50
|
end
|
|
23
51
|
|
|
52
|
+
# Create a new address
|
|
53
|
+
#
|
|
54
|
+
# @param post_office_box [String] Post office box
|
|
55
|
+
# @param extended_address [String] Extended address (e.g., apartment, suite)
|
|
56
|
+
# @param street_address [String] Street address
|
|
57
|
+
# @param locality [String] City or locality
|
|
58
|
+
# @param region [String] State, province, or region
|
|
59
|
+
# @param postal_code [String] ZIP or postal code
|
|
60
|
+
# @param country [String] Country
|
|
61
|
+
# @param options [Hash] Additional options
|
|
62
|
+
# @option options [Integer] :position Position in collection (1-based, used internally)
|
|
63
|
+
# @raise [ArgumentError] if position is invalid (< 1)
|
|
64
|
+
# @return [Address]
|
|
24
65
|
def initialize(post_office_box:, extended_address:, street_address:, locality:, region:, postal_code:, country:, **options)
|
|
25
66
|
@post_office_box = post_office_box
|
|
26
67
|
@extended_address = extended_address
|
|
@@ -30,7 +71,7 @@ module Valerie
|
|
|
30
71
|
@postal_code = postal_code
|
|
31
72
|
@country = country
|
|
32
73
|
@options = options
|
|
33
|
-
|
|
74
|
+
|
|
34
75
|
raise ArgumentError, 'Invalid Position' if invalid_position?
|
|
35
76
|
end
|
|
36
77
|
|
|
@@ -40,14 +81,14 @@ module Valerie
|
|
|
40
81
|
|
|
41
82
|
def to_s
|
|
42
83
|
parts = ['ADR']
|
|
43
|
-
|
|
44
|
-
parts << "
|
|
45
|
-
|
|
84
|
+
|
|
85
|
+
parts << "PREF=#{position}" if position?
|
|
86
|
+
|
|
46
87
|
@options.map do |key, value|
|
|
47
88
|
next if key == :position
|
|
48
89
|
parts << "#{key}=#{value}"
|
|
49
90
|
end
|
|
50
|
-
|
|
91
|
+
|
|
51
92
|
parts.join(';') + ":#{identifier}"
|
|
52
93
|
end
|
|
53
94
|
|
data/lib/valerie/card.rb
CHANGED
|
@@ -2,25 +2,98 @@ require_relative '../valerie'
|
|
|
2
2
|
require_relative 'core/parser'
|
|
3
3
|
|
|
4
4
|
module Valerie
|
|
5
|
+
# Represents a VCard (Contact Card) that can be generated or parsed.
|
|
6
|
+
#
|
|
7
|
+
# A Card contains various contact properties like name, email addresses,
|
|
8
|
+
# phone numbers, addresses, and more. It can be converted to VCard 3.0 format
|
|
9
|
+
# or parsed from VCard strings.
|
|
10
|
+
#
|
|
11
|
+
# @example Creating a new card
|
|
12
|
+
# card = Valerie::Card.new
|
|
13
|
+
# card.name = { first_name: 'Jane', last_name: 'Smith' }
|
|
14
|
+
# card.organization = { name: 'Acme Corp', department: 'Engineering' }
|
|
15
|
+
# card.birthday = Date.new(1990, 5, 15)
|
|
16
|
+
# card.gender = 'female'
|
|
17
|
+
#
|
|
18
|
+
# @example Adding contacts
|
|
19
|
+
# card.emails.add('jane@example.com', type: 'work')
|
|
20
|
+
# card.phones.add('+1-555-1234', type: 'cell')
|
|
21
|
+
# card.addresses.add(
|
|
22
|
+
# street_address: '123 Main St',
|
|
23
|
+
# locality: 'New York',
|
|
24
|
+
# region: 'NY',
|
|
25
|
+
# postal_code: '10001',
|
|
26
|
+
# country: 'USA'
|
|
27
|
+
# )
|
|
28
|
+
#
|
|
29
|
+
# @example Generating VCard output
|
|
30
|
+
# vcard_string = card.to_s
|
|
31
|
+
#
|
|
32
|
+
# @example Parsing a VCard
|
|
33
|
+
# cards = Valerie::Card.parse("BEGIN:VCARD\r\n...")
|
|
5
34
|
class Card
|
|
6
35
|
extend Core::Parser
|
|
7
|
-
|
|
36
|
+
|
|
37
|
+
# @return [Name, nil] The name of the contact
|
|
38
|
+
# @return [String, nil] The formatted full name (FN field)
|
|
39
|
+
# @return [Organization, nil] The organization details
|
|
40
|
+
# @return [Gender, nil] The gender
|
|
41
|
+
# @return [Birthday, nil] The birthday
|
|
8
42
|
attr_reader :name, :formatted_name, :organization, :gender, :birthday
|
|
9
|
-
|
|
43
|
+
|
|
44
|
+
# Set the formatted name (FN field) for this card
|
|
45
|
+
#
|
|
46
|
+
# @param value [String] The formatted full name
|
|
47
|
+
# @return [String] The formatted name
|
|
48
|
+
# @example
|
|
49
|
+
# card.formatted_name = 'Dr. Jane Smith Jr.'
|
|
50
|
+
def formatted_name=(value)
|
|
51
|
+
@formatted_name = value
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Set the name for this card
|
|
55
|
+
#
|
|
56
|
+
# Automatically sets the formatted_name if not already set.
|
|
57
|
+
#
|
|
58
|
+
# @param parts [Hash, String, Name] Name data
|
|
59
|
+
# @option parts [String] :first_name Given name
|
|
60
|
+
# @option parts [String] :last_name Family name
|
|
61
|
+
# @option parts [String] :middle_name Middle name (optional)
|
|
62
|
+
# @option parts [String] :prefix Prefix like "Dr." or "Ms." (optional)
|
|
63
|
+
# @option parts [String] :suffix Suffix like "Jr." or "PhD" (optional)
|
|
64
|
+
# @return [Name] The created Name object
|
|
65
|
+
# @example With hash
|
|
66
|
+
# card.name = { first_name: 'John', last_name: 'Doe' }
|
|
67
|
+
# @example With string (splits on space)
|
|
68
|
+
# card.name = 'John Doe'
|
|
69
|
+
# @example With Name object
|
|
70
|
+
# card.name = Valerie::Name.new(first_name: 'John', last_name: 'Doe')
|
|
10
71
|
def name=(parts)
|
|
11
72
|
if parts.instance_of?(Name)
|
|
12
73
|
@name = parts
|
|
13
74
|
else
|
|
14
75
|
@name = Name.new parts
|
|
15
76
|
end
|
|
16
|
-
|
|
77
|
+
|
|
17
78
|
if (@name.first_name || @name.last_name) && @formatted_name.nil?
|
|
18
79
|
@formatted_name = "#{@name.first_name} #{@name.last_name}".strip
|
|
19
80
|
end
|
|
20
|
-
|
|
81
|
+
|
|
21
82
|
@name
|
|
22
83
|
end
|
|
23
|
-
|
|
84
|
+
|
|
85
|
+
# Set the organization for this card
|
|
86
|
+
#
|
|
87
|
+
# @param args [Hash, Array, String, Organization] Organization data
|
|
88
|
+
# @option args [String] :name Organization name
|
|
89
|
+
# @option args [String] :department Department name (optional)
|
|
90
|
+
# @return [Organization] The created Organization object
|
|
91
|
+
# @example With hash
|
|
92
|
+
# card.organization = { name: 'Acme Corp', department: 'Engineering' }
|
|
93
|
+
# @example With array
|
|
94
|
+
# card.organization = ['Acme Corp', 'Engineering']
|
|
95
|
+
# @example With string (department will be empty)
|
|
96
|
+
# card.organization = 'Acme Corp'
|
|
24
97
|
def organization=(*args)
|
|
25
98
|
if args.first.instance_of?(Organization)
|
|
26
99
|
@organization = args.flatten.first
|
|
@@ -28,55 +101,99 @@ module Valerie
|
|
|
28
101
|
@organization = Organization.new(*args)
|
|
29
102
|
end
|
|
30
103
|
end
|
|
31
|
-
|
|
104
|
+
|
|
105
|
+
# Set the gender for this card
|
|
106
|
+
#
|
|
107
|
+
# @param value [String, Symbol, Gender] Gender identifier
|
|
108
|
+
# Valid values: 'male', 'female', 'other', 'none', 'unknown', 'M', 'F', 'O', 'N', 'U'
|
|
109
|
+
# @return [Gender] The created Gender object
|
|
110
|
+
# @raise [ArgumentError] if the gender identifier is invalid
|
|
111
|
+
# @example
|
|
112
|
+
# card.gender = 'male'
|
|
113
|
+
# card.gender = :female
|
|
32
114
|
def gender=(value)
|
|
33
115
|
@gender = value.is_a?(Gender) ? value : Gender.new(value)
|
|
34
116
|
end
|
|
35
|
-
|
|
117
|
+
|
|
118
|
+
# Set the birthday for this card
|
|
119
|
+
#
|
|
120
|
+
# @param value [Date, Time, String, Birthday] Birthday value
|
|
121
|
+
# If the value responds to strftime, it will be formatted as YYYY-MM-DD
|
|
122
|
+
# @return [Birthday] The created Birthday object
|
|
123
|
+
# @example With Date object
|
|
124
|
+
# card.birthday = Date.new(1990, 5, 15)
|
|
125
|
+
# @example With string
|
|
126
|
+
# card.birthday = '1990-05-15'
|
|
36
127
|
def birthday=(value)
|
|
37
128
|
@birthday = value.is_a?(Birthday) ? value : Birthday.new(value)
|
|
38
129
|
end
|
|
39
|
-
|
|
130
|
+
|
|
131
|
+
# Get the email collection for this card
|
|
132
|
+
#
|
|
133
|
+
# @return [Collection::EmailCollection] The email collection
|
|
134
|
+
# @example
|
|
135
|
+
# card.emails.add('user@example.com', type: 'work')
|
|
40
136
|
def emails
|
|
41
137
|
@emails ||= Collection::EmailCollection.new
|
|
42
138
|
end
|
|
43
|
-
|
|
139
|
+
|
|
140
|
+
# Get the address collection for this card
|
|
141
|
+
#
|
|
142
|
+
# @return [Collection::AddressCollection] The address collection
|
|
143
|
+
# @example
|
|
144
|
+
# card.addresses.add(street_address: '123 Main St', locality: 'NYC', ...)
|
|
44
145
|
def addresses
|
|
45
146
|
@addresses ||= Collection::AddressCollection.new
|
|
46
147
|
end
|
|
47
|
-
|
|
148
|
+
|
|
149
|
+
# Get the phone collection for this card
|
|
150
|
+
#
|
|
151
|
+
# @return [Collection::PhoneCollection] The phone collection
|
|
152
|
+
# @example
|
|
153
|
+
# card.phones.add('+1-555-1234', type: 'cell')
|
|
48
154
|
def phones
|
|
49
155
|
@phones ||= Collection::PhoneCollection.new
|
|
50
156
|
end
|
|
51
|
-
|
|
157
|
+
|
|
158
|
+
# Convert this card to a VCard 3.0 formatted string
|
|
159
|
+
#
|
|
160
|
+
# @return [String] VCard string with \r\n line endings
|
|
161
|
+
# @example
|
|
162
|
+
# vcard_string = card.to_s
|
|
163
|
+
# File.write('contact.vcf', vcard_string)
|
|
52
164
|
def to_s
|
|
53
165
|
to_a.join("\r\n")
|
|
54
166
|
end
|
|
55
|
-
|
|
167
|
+
|
|
168
|
+
# Convert this card to an array of VCard lines
|
|
169
|
+
#
|
|
170
|
+
# @return [Array<String>] Array of VCard property lines
|
|
171
|
+
# @api private
|
|
56
172
|
def to_a
|
|
57
173
|
parts = [
|
|
58
174
|
'BEGIN:VCARD',
|
|
59
175
|
"VERSION:#{Valerie.configuration.version}",
|
|
60
176
|
"PRODID:-//#{Valerie.configuration.product}//#{Valerie.configuration.language}",
|
|
61
177
|
]
|
|
62
|
-
|
|
178
|
+
|
|
63
179
|
parts << @name.to_s if @name
|
|
180
|
+
parts << "FN:#{@formatted_name}" if @formatted_name
|
|
64
181
|
parts << @organization.to_s if @organization
|
|
65
182
|
parts << @birthday.to_s if @birthday
|
|
66
183
|
parts << @gender.to_s if @gender
|
|
67
|
-
|
|
184
|
+
|
|
68
185
|
emails.each do |email|
|
|
69
186
|
parts << email.to_s
|
|
70
187
|
end
|
|
71
|
-
|
|
188
|
+
|
|
72
189
|
phones.each do |phone|
|
|
73
190
|
parts << phone.to_s
|
|
74
191
|
end
|
|
75
|
-
|
|
192
|
+
|
|
76
193
|
addresses.each do |address|
|
|
77
194
|
parts << address.to_s
|
|
78
195
|
end
|
|
79
|
-
|
|
196
|
+
|
|
80
197
|
parts << 'END:VCARD'
|
|
81
198
|
parts
|
|
82
199
|
end
|
|
@@ -3,17 +3,49 @@ require_relative '../address'
|
|
|
3
3
|
|
|
4
4
|
module Valerie
|
|
5
5
|
module Collection
|
|
6
|
+
# Collection of addresses for a VCard
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# collection = Valerie::Collection::AddressCollection.new
|
|
10
|
+
# collection.add(
|
|
11
|
+
# street_address: '123 Main St',
|
|
12
|
+
# locality: 'New York',
|
|
13
|
+
# region: 'NY',
|
|
14
|
+
# postal_code: '10001',
|
|
15
|
+
# country: 'USA'
|
|
16
|
+
# )
|
|
6
17
|
class AddressCollection < Base
|
|
7
|
-
|
|
18
|
+
# Add an address to the collection
|
|
19
|
+
#
|
|
20
|
+
# Addresses are automatically sorted by position after adding.
|
|
21
|
+
#
|
|
22
|
+
# @param address [Hash, Address, nil] Address data hash or Address object
|
|
23
|
+
# @param options [Hash] Additional options merged with address data
|
|
24
|
+
# @option options [Integer] :position Position in collection (1-based, auto-assigned if not specified)
|
|
25
|
+
# @return [Address] The added address object
|
|
26
|
+
# @example Add with hash
|
|
27
|
+
# collection.add(
|
|
28
|
+
# post_office_box: '',
|
|
29
|
+
# extended_address: '',
|
|
30
|
+
# street_address: '123 Main St',
|
|
31
|
+
# locality: 'New York',
|
|
32
|
+
# region: 'NY',
|
|
33
|
+
# postal_code: '10001',
|
|
34
|
+
# country: 'USA'
|
|
35
|
+
# )
|
|
36
|
+
def add(address = nil, **options)
|
|
8
37
|
@item = if address.is_a?(Address)
|
|
9
|
-
address.dup { _1.position = options[:position] || @items.size + 1 }
|
|
38
|
+
address.dup.tap { _1.position = options[:position] || @items.size + 1 }
|
|
10
39
|
else
|
|
11
|
-
|
|
40
|
+
merged_options = (address || {}).merge(options)
|
|
41
|
+
merged_options[:position] ||= @items.size + 1
|
|
42
|
+
|
|
43
|
+
Address.new(**merged_options)
|
|
12
44
|
end
|
|
13
|
-
|
|
45
|
+
|
|
14
46
|
@items << @item
|
|
15
47
|
@items = @items.sort_by(&:position)
|
|
16
|
-
|
|
48
|
+
|
|
17
49
|
@item
|
|
18
50
|
end
|
|
19
51
|
end
|
|
@@ -3,7 +3,27 @@ require_relative '../email'
|
|
|
3
3
|
|
|
4
4
|
module Valerie
|
|
5
5
|
module Collection
|
|
6
|
+
# Collection of email addresses for a VCard
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# collection = Valerie::Collection::EmailCollection.new
|
|
10
|
+
# collection.add('work@example.com', type: :work)
|
|
11
|
+
# collection.add('personal@example.com', type: :home, position: 2)
|
|
6
12
|
class EmailCollection < Base
|
|
13
|
+
# Add an email address to the collection
|
|
14
|
+
#
|
|
15
|
+
# Email addresses are automatically sorted by position after adding.
|
|
16
|
+
#
|
|
17
|
+
# @param email [String, Email] Email address string or Email object
|
|
18
|
+
# @param options [Hash] Options (only used if email is a String)
|
|
19
|
+
# @option options [String, Symbol, Array] :type Email type (:work, :home, :internet, etc.)
|
|
20
|
+
# @option options [Integer] :position Position in collection (1-based, auto-assigned if not specified)
|
|
21
|
+
# @return [Email] The added email object
|
|
22
|
+
# @example Add with type
|
|
23
|
+
# collection.add('user@example.com', type: :work)
|
|
24
|
+
# @example Add Email object
|
|
25
|
+
# email = Valerie::Email.new(address: 'user@example.com', type: :work)
|
|
26
|
+
# collection.add(email)
|
|
7
27
|
def add(email, **options)
|
|
8
28
|
@item = if email.is_a?(Email)
|
|
9
29
|
email.dup.tap { _1.position = options[:position] || @items.size + 1 }
|
|
@@ -12,10 +32,10 @@ module Valerie
|
|
|
12
32
|
else
|
|
13
33
|
Email.new(address: email, **options, position: options[:position] || @items.size + 1)
|
|
14
34
|
end
|
|
15
|
-
|
|
35
|
+
|
|
16
36
|
@items << @item
|
|
17
37
|
@items = @items.sort_by(&:position)
|
|
18
|
-
|
|
38
|
+
|
|
19
39
|
@item
|
|
20
40
|
end
|
|
21
41
|
end
|
|
@@ -3,17 +3,36 @@ require_relative '../phone'
|
|
|
3
3
|
|
|
4
4
|
module Valerie
|
|
5
5
|
module Collection
|
|
6
|
+
# Collection of phone numbers for a VCard
|
|
7
|
+
#
|
|
8
|
+
# @example
|
|
9
|
+
# collection = Valerie::Collection::PhoneCollection.new
|
|
10
|
+
# collection.add('+1-555-1234', type: :work)
|
|
11
|
+
# collection.add('+1-555-9999', type: :cell, position: 1)
|
|
6
12
|
class PhoneCollection < Base
|
|
13
|
+
# Add a phone number to the collection
|
|
14
|
+
#
|
|
15
|
+
# Phone numbers are automatically sorted by position after adding.
|
|
16
|
+
#
|
|
17
|
+
# @param phone [String, Phone] Phone number string or Phone object
|
|
18
|
+
# @param options [Hash] Options (only used if phone is a String)
|
|
19
|
+
# @option options [String, Symbol, Array] :type Phone type (:work, :home, :cell, etc.)
|
|
20
|
+
# @option options [Integer] :position Position in collection (1-based, auto-assigned if not specified)
|
|
21
|
+
# @return [Phone] The added phone object
|
|
22
|
+
# @example Add with type
|
|
23
|
+
# collection.add('+1-555-1234', type: :work)
|
|
24
|
+
# @example Add with position
|
|
25
|
+
# collection.add('+1-555-1234', type: :cell, position: 1)
|
|
7
26
|
def add(phone, **options)
|
|
8
27
|
@item = if phone.is_a?(Phone)
|
|
9
28
|
phone.dup.tap { _1.position = options[:position] || @items.size + 1 }
|
|
10
29
|
else
|
|
11
30
|
Phone.new(phone, **options, position: options[:position] || @items.size + 1)
|
|
12
31
|
end
|
|
13
|
-
|
|
32
|
+
|
|
14
33
|
@items << @item
|
|
15
34
|
@items = @items.sort_by(&:position)
|
|
16
|
-
|
|
35
|
+
|
|
17
36
|
@item
|
|
18
37
|
end
|
|
19
38
|
end
|