Skip to content
Merged

V1 #2

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,20 @@
.idea/*
Gemfile.lock
# YARD documentation
/.yardoc
/doc

# RubyGems
/*.gem

# Bundler
/.bundle
/vendor/bundle

# Test coverage
/coverage

# IDE
/.idea
/.vscode
*.swp
*.swo
*~
10 changes: 10 additions & 0 deletions .yardopts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
--markup markdown
--markup-provider kramdown
--output-dir doc
--protected
--private
--title "Valerie - VCard Parser and Generator Documentation"
--readme README.md
lib/**/*.rb
-
LICENSE
24 changes: 24 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
PATH
remote: .
specs:
valerie (1.0.0)

GEM
remote: https://rubygems.org/
specs:
minitest (5.26.0)
rake (13.3.1)
yard (0.9.37)

PLATFORMS
arm64-darwin-24
ruby

DEPENDENCIES
minitest (~> 5.14)
rake (~> 13.0)
valerie!
yard (~> 0.9)

BUNDLED WITH
2.6.7
43 changes: 30 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,15 @@ Add this line to your application's Gemfile:
gem 'valerie'
```

Or install it yourself as:
Or install it yourself as:

```bash
gem install valerie
```

## Usage

Parsing a VCard is as simple as passing a VCard string, like
Parsing a VCard is as simple as passing a VCard string, like

```ruby
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"
Expand All @@ -42,8 +42,7 @@ According to the VCard 3.0 specification, some types of properties can have mult
- `TEL`: for telephone numbers
- `ADR`: for addresses

Aside from these, every other support property supports a single value.

Aside from these, every other support property supports a single value.

### Single value properties

Expand All @@ -69,9 +68,9 @@ Or an array with the following order `[first_name, last_name, middle_name, prefi
card.name = %w[Shira Rosenbaum M Ms. PhD]
```

#### Gender
#### Gender

To set the Gender of the card you can, set it value `Card#gender=`, this accepts one of the following constants:
To set the Gender of the card you can, set it value `Card#gender=`, this accepts one of the following constants:
`male`, `female`, `other`, `none` and `unknown`. Passing another value raises an `ArgumentError`.

```ruby
Expand All @@ -97,7 +96,7 @@ card.organization = { organization: 'Hellotext', department: 'Engineering' }
card.organization = 'Hellotext'
```

### Collections
### Collections

The card exposes methods to adding the respective collectable properties. Email, telephone and address.

Expand Down Expand Up @@ -129,11 +128,11 @@ card.addresses.add(
)
```

#### Positions
#### Positions

Emails, phones and addresses are ordered. They can include an optional `position` argument
Emails, phones and addresses are ordered. They can include an optional `position` argument
to specify their order when the profile has multiple values. Whenever you add a new value,
a default position argument is picked and the value is appended as the last element.
a default position argument is picked and the value is appended as the last element.

To specify the position, you can pass the `position` argument as a keyword argument. Unlike arrays
the position is 1-based and not 0-based.
Expand All @@ -153,9 +152,9 @@ card.emails.add('user@domain.com', type: 'work')
card.emails.add('user@domain.com', type: %w[work internet])
```

### Configuration
### Configuration

You can configure the following properties of the card.
You can configure the following properties of the card.

- `prodid`: The product id of the card. This defaults to 'Valerie www.hellotext.com'.
- `version`: The version of the card. This defaults to '3.0'.
Expand All @@ -169,6 +168,24 @@ Valerie.configure do |config|
end
```

### Documentation

Valerie uses [YARD](https://yardoc.org/) for API documentation. To generate the documentation locally:

```bash
# Install dependencies
bundle install

# Generate documentation
bundle exec rake yard

# Or generate and view
bundle exec rake doc
open doc/index.html
```

The documentation covers all public APIs with examples and usage information.

### Licence

This code is released under the MIT License. See the LICENSE file for more information.
Expand All @@ -181,7 +198,7 @@ This code is released under the MIT License. See the LICENSE file for more infor

Contributions are welcome. Please follow the steps below to contribute.

1. Fork it
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
Expand Down
24 changes: 24 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,27 @@ end

desc 'Run tests'
task default: :test

# YARD documentation task
begin
require 'yard'

YARD::Rake::YardocTask.new do |t|
t.files = ['lib/**/*.rb']
t.options = ['--markup', 'markdown', '--title', 'Valerie Documentation']
end

desc 'Generate YARD documentation and open in browser'
task :doc => :yard do
puts "Documentation generated in doc/"
puts "Run 'open doc/index.html' to view"
end
rescue LoadError
# YARD not available
desc 'Generate YARD documentation (YARD not installed)'
task :yard do
puts "YARD is not available. Install it with: gem install yard"
end

task :doc => :yard
end
50 changes: 43 additions & 7 deletions lib/valerie.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,28 +11,64 @@
require 'valerie/collection/address_collection'
require 'valerie/collection/phone_collection'

# Valerie is a VCard 3.0 parser and generator for Ruby.
# It provides a simple and flexible API for creating, parsing, and managing contact cards.
#
# @example Basic usage
# card = Valerie::Card.new
# card.name = { first_name: 'John', last_name: 'Doe' }
# card.emails.add('john@example.com', type: 'work')
# puts card.to_s
#
# @example Parsing a VCard
# vcard_string = "BEGIN:VCARD\r\nVERSION:3.0\r\n..."
# cards = Valerie::Card.parse(vcard_string)
#
# @see https://github.com/hellotext/valerie
module Valerie
VERSION = '0.0.7'.freeze

# Current version of the Valerie gem
VERSION = '1.0.0'.freeze

# Get the global configuration object
#
# @return [Configuration] The global configuration
def self.configuration
@configuration ||= Configuration.new
end


# Configure Valerie globally
#
# @yield [Configuration] The configuration object
# @example
# Valerie.configure do |config|
# config.product = 'My App'
# config.version = '4.0'
# end
def self.configure
yield(configuration)
end


# Global configuration for VCard generation
class Configuration
# @return [String] Product identifier for PRODID field
# @return [String] VCard version number
# @return [String] Language code for the card
attr_accessor :product, :version, :language


# Get the product identifier (defaults to 'Valerie www.hellotext.com')
# @return [String]
def product
@product ||= 'Valerie www.hellotext.com'
end


# Get the VCard version (defaults to '3.0')
# @return [String]
def version
@version ||= '3.0'
end


# Get the language code (defaults to 'EN')
# @return [String]
def language
@language ||= 'EN'
end
Expand Down
53 changes: 47 additions & 6 deletions lib/valerie/address.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,37 @@
require_relative 'ordered'

module Valerie
# Represents a physical address in a VCard
#
# @example Full address
# address = Valerie::Address.new(
# post_office_box: 'PO Box 123',
# extended_address: 'Suite 200',
# street_address: '123 Main St',
# locality: 'New York',
# region: 'NY',
# postal_code: '10001',
# country: 'USA'
# )
#
# @example Simple address
# address = Valerie::Address.new(
# post_office_box: '',
# extended_address: '',
# street_address: '123 Main St',
# locality: 'New York',
# region: 'NY',
# postal_code: '10001',
# country: 'USA'
# )
class Address
include Ordered


# Parse an address from VCard ADR field string
#
# @param data [String] ADR field string
# @return [Address] Parsed address object
# @api private
def self.from_s(data)
data = data[data.index("ADR;")..] unless data.start_with?("ADR;")
identifier = data.split(":").last.split(";")
Expand All @@ -21,6 +49,19 @@ def self.from_s(data)
)
end

# Create a new address
#
# @param post_office_box [String] Post office box
# @param extended_address [String] Extended address (e.g., apartment, suite)
# @param street_address [String] Street address
# @param locality [String] City or locality
# @param region [String] State, province, or region
# @param postal_code [String] ZIP or postal code
# @param country [String] Country
# @param options [Hash] Additional options
# @option options [Integer] :position Position in collection (1-based, used internally)
# @raise [ArgumentError] if position is invalid (< 1)
# @return [Address]
def initialize(post_office_box:, extended_address:, street_address:, locality:, region:, postal_code:, country:, **options)
@post_office_box = post_office_box
@extended_address = extended_address
Expand All @@ -30,7 +71,7 @@ def initialize(post_office_box:, extended_address:, street_address:, locality:,
@postal_code = postal_code
@country = country
@options = options

raise ArgumentError, 'Invalid Position' if invalid_position?
end

Expand All @@ -40,14 +81,14 @@ def [](key)

def to_s
parts = ['ADR']
parts << "PERF=#{position}" if position?

parts << "PREF=#{position}" if position?

@options.map do |key, value|
next if key == :position
parts << "#{key}=#{value}"
end

parts.join(';') + ":#{identifier}"
end

Expand Down
Loading