treecard 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/README.md ADDED
@@ -0,0 +1 @@
1
+ At some point this will be documented. But not today.
@@ -0,0 +1,20 @@
1
+ # A line in the VCard file.
2
+ #
3
+ # * +name+ is the name of the attribute, like 'fn', 'adr', etc.
4
+ # * +param+ is a list of +TreeCard::Param+ objects, representing each
5
+ # parameter associated with this attribute
6
+ # * +value+ is the raw text of the attribute's value. It is up to
7
+ # the caller to parse, if necessary.
8
+ class TreeCard::Attribute
9
+
10
+ attr_reader :name, :params, :value
11
+
12
+ def initialize(name, params, value)
13
+ @name = name
14
+ @params = {}
15
+ params.each do |param|
16
+ @params[param.name.downcase] = param
17
+ end
18
+ @value = value
19
+ end
20
+ end
@@ -0,0 +1,16 @@
1
+ class TreeCard::ContentLineNode < Treetop::Runtime::SyntaxNode
2
+
3
+ # Returns the TreeCard::Attribute representing this node.
4
+ def attribute
5
+ TreeCard::Attribute.new(name.text_value, param_objects, value.text_value)
6
+ end
7
+
8
+ private
9
+
10
+ def param_objects
11
+ param_list.elements.map do |semicolon_and_param|
12
+ semicolon_and_param.param.param_object
13
+ end
14
+ end
15
+
16
+ end
@@ -0,0 +1,11 @@
1
+ class TreeCard::ParamNode < Treetop::Runtime::SyntaxNode
2
+
3
+ # Returns the TreeCard::Param object representing this node.
4
+ def param_object
5
+ param_values = []
6
+ if self.respond_to?(:param_value)
7
+ param_values = [param_value.text_value] + extra_params.elements.map { |extra_param| extra_param.param_value.text_value }
8
+ end
9
+ TreeCard::Param.new(param_name.text_value, param_values)
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ # A VCard attribute's parameter. Just contains a key, called +name+,
2
+ # and a list of values.
3
+ class TreeCard::Param
4
+ attr_reader :name, :values
5
+ def initialize(name, values)
6
+ @name = name.downcase
7
+ @values = values
8
+ end
9
+ end
data/lib/treecard.rb ADDED
@@ -0,0 +1,109 @@
1
+ require 'treetop'
2
+ require 'grammar/vcard' # implements the vcard spec, found at http://tools.ietf.org/html/rfc2425
3
+
4
+ class TreeCard; end
5
+
6
+ require 'treecard/nodes/content_line_node'
7
+ require 'treecard/nodes/param_node'
8
+ require 'treecard/attribute'
9
+ require 'treecard/param'
10
+
11
+ class TreeCard
12
+
13
+ attr_reader :raw_data
14
+ attr_reader :attributes
15
+
16
+ def initialize(data)
17
+ @attributes = Hash.new {|h, k| h[k] = []}
18
+ @raw_data = self.class.unfold_lines(data)
19
+ @parser = VCardGrammarParser.new
20
+ ast = @parser.parse(@raw_data)
21
+ if ast
22
+ parse(ast)
23
+ else
24
+ raise TreeCard::ParseError, @parser.failure_reason
25
+ end
26
+ end
27
+
28
+ def name
29
+ attr = attributes['fn']
30
+ attr &&= attr.first
31
+ attr.value if attr
32
+ end
33
+
34
+ def emails
35
+ attr = attributes['email']
36
+ attr.map {|email| email.value} if attr
37
+ end
38
+
39
+ def phone
40
+ attr = attribute_with_param('tel', 'voice')
41
+ attr.value if attr
42
+ end
43
+
44
+ def fax
45
+ attr = attribute_with_param('tel', 'fax')
46
+ attr.value if attr
47
+ end
48
+
49
+ def photo
50
+ attr = attributes['photo']
51
+ attr &&= attr.first
52
+ attr.value if attr
53
+ end
54
+
55
+ def address
56
+ attr = attributes['adr']
57
+ attr &&= attr.first
58
+ attr.value.split(';').join("\n").strip if attr
59
+ end
60
+
61
+ def company
62
+ attr = attributes['org']
63
+ attr &&= attr.first
64
+ attr.value if attr
65
+ end
66
+
67
+ # VCards automatically wrap lines longer than 75 characters. Wrapped
68
+ # lines are signified by leading whitespace.
69
+ def self.unfold_lines(data)
70
+ unfolded_data = ""
71
+ current_line = ""
72
+ data.lines.each do |line|
73
+ if line =~ /^\s\S+/
74
+ current_line << line[1..-1].chomp
75
+ else
76
+ if current_line != ""
77
+ if current_line =~ /=0D=0A=$/ #weird quoted printable thing
78
+ unfolded_data << current_line.chomp
79
+ else
80
+ unfolded_data << current_line + "\n"
81
+ end
82
+ end
83
+ current_line = line.chomp
84
+ end
85
+ end
86
+ unfolded_data << current_line + "\n"
87
+ unfolded_data
88
+ end
89
+
90
+ # Returns the first attribute with +attr_name+ where +param_name+ is
91
+ # set.
92
+ def attribute_with_param(attr_name, param_name)
93
+ attributes = self.attributes[attr_name.downcase]
94
+ found_attributes = attributes.select { |attribute| attribute.params[param_name.downcase] }
95
+ found_attributes.first
96
+ end
97
+
98
+ private
99
+
100
+ def parse(root)
101
+ root.elements.each do |contentline|
102
+ parse_contentline(contentline)
103
+ end
104
+ end
105
+
106
+ def parse_contentline(node)
107
+ attributes[node.name.text_value.downcase] << node.attribute
108
+ end
109
+ end
@@ -0,0 +1,12 @@
1
+ require 'shoulda'
2
+ require 'treecard'
3
+ require 'pathname'
4
+
5
+ class Test::Unit::TestCase
6
+
7
+ @@my_path = Pathname.new(File.expand_path(File.dirname(__FILE__)))
8
+
9
+ def vcard_data(vcard_file_name)
10
+ File.read(@@my_path + 'data' + vcard_file_name)
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ require 'test_helper'
2
+
3
+ class TreeCardTest < Test::Unit::TestCase
4
+
5
+ context "A new TreeCard populated with data" do
6
+ setup do
7
+ @vcard = TreeCard.new(vcard_data('Mark_Ericsson.vcf'))
8
+ end
9
+
10
+ should "unfold its lines" do
11
+ assert_equal "REV:20090729T163338Z", @vcard.raw_data.split("\n")[15]
12
+ end
13
+
14
+ should "parse its data" do
15
+ assert_equal "Mark S. Ericsson", @vcard.name
16
+ assert_equal ["mericsson@example.com"], @vcard.emails
17
+ assert_equal "(925) 930-6000", @vcard.phone
18
+ assert_equal "(925) 934-5377", @vcard.fax
19
+ assert_equal "Youngman, Ericsson & Low, LLP", @vcard.company
20
+ assert_equal "1981 N. Broadway, Suite 300\nWalnut Creek\nCA\n94596-3841\nUnited States of America", @vcard.address
21
+ end
22
+ end
23
+
24
+ context "TreeCard#unfold_lines" do
25
+ should "unfold lines that begin with a single whitespace character" do
26
+ data = "This is the first line\nThis is a long descrip\n tion that exists o\n\tn a long line"
27
+ assert_equal "This is the first line\nThis is a long description that exists on a long line\n", TreeCard.unfold_lines(data)
28
+ end
29
+
30
+ should "not unfold if there is no real data on a line" do
31
+ data = "This is another line\n \n"
32
+ assert_equal "This is another line\n \n", TreeCard.unfold_lines(data)
33
+ end
34
+
35
+ should "not unfold more than one whitespace character" do
36
+ data = "This is a line\n test text"
37
+ assert_equal "This is a line\n test text\n", TreeCard.unfold_lines(data)
38
+ end
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,105 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: treecard
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 2
10
+ version: 0.0.2
11
+ platform: ruby
12
+ authors:
13
+ - Justin Weiss
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-07-21 00:00:00 -07:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: treetop
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 23
30
+ segments:
31
+ - 1
32
+ - 4
33
+ - 8
34
+ version: 1.4.8
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: shoulda
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ">="
44
+ - !ruby/object:Gem::Version
45
+ hash: 33
46
+ segments:
47
+ - 2
48
+ - 10
49
+ - 3
50
+ version: 2.10.3
51
+ type: :development
52
+ version_requirements: *id002
53
+ description:
54
+ email: jweiss@avvo.com
55
+ executables: []
56
+
57
+ extensions: []
58
+
59
+ extra_rdoc_files:
60
+ - README.md
61
+ files:
62
+ - lib/treecard/attribute.rb
63
+ - lib/treecard/nodes/content_line_node.rb
64
+ - lib/treecard/nodes/param_node.rb
65
+ - lib/treecard/param.rb
66
+ - lib/treecard.rb
67
+ - test/test_helper.rb
68
+ - test/unit/treecard_test.rb
69
+ - README.md
70
+ has_rdoc: true
71
+ homepage:
72
+ licenses: []
73
+
74
+ post_install_message:
75
+ rdoc_options: []
76
+
77
+ require_paths:
78
+ - lib
79
+ required_ruby_version: !ruby/object:Gem::Requirement
80
+ none: false
81
+ requirements:
82
+ - - ">="
83
+ - !ruby/object:Gem::Version
84
+ hash: 3
85
+ segments:
86
+ - 0
87
+ version: "0"
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ hash: 3
94
+ segments:
95
+ - 0
96
+ version: "0"
97
+ requirements: []
98
+
99
+ rubyforge_project:
100
+ rubygems_version: 1.3.7
101
+ signing_key:
102
+ specification_version: 3
103
+ summary: A simple vcard parser using Treetop
104
+ test_files: []
105
+