treecard 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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
+