virginity 0.3.31

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.
Files changed (42) hide show
  1. checksums.yaml +7 -0
  2. data/lib/virginity.rb +6 -0
  3. data/lib/virginity/api_extensions.rb +87 -0
  4. data/lib/virginity/api_extensions/fields_to_json.rb +82 -0
  5. data/lib/virginity/api_extensions/fields_to_xml.rb +151 -0
  6. data/lib/virginity/bnf.rb +84 -0
  7. data/lib/virginity/dir_info.rb +93 -0
  8. data/lib/virginity/dir_info/content_line.rb +146 -0
  9. data/lib/virginity/dir_info/line_folding.rb +60 -0
  10. data/lib/virginity/dir_info/param.rb +208 -0
  11. data/lib/virginity/dir_info/query.rb +144 -0
  12. data/lib/virginity/encoding_decoding.rb +177 -0
  13. data/lib/virginity/encodings.rb +36 -0
  14. data/lib/virginity/fixes.rb +230 -0
  15. data/lib/virginity/vcard.rb +244 -0
  16. data/lib/virginity/vcard/base_field.rb +126 -0
  17. data/lib/virginity/vcard/categories.rb +57 -0
  18. data/lib/virginity/vcard/cleaning.rb +364 -0
  19. data/lib/virginity/vcard/field.rb +22 -0
  20. data/lib/virginity/vcard/field/params.rb +93 -0
  21. data/lib/virginity/vcard/field_values.rb +10 -0
  22. data/lib/virginity/vcard/field_values/binary.rb +22 -0
  23. data/lib/virginity/vcard/field_values/boolean.rb +14 -0
  24. data/lib/virginity/vcard/field_values/case_insensitive_value.rb +13 -0
  25. data/lib/virginity/vcard/field_values/date.rb +16 -0
  26. data/lib/virginity/vcard/field_values/integer.rb +15 -0
  27. data/lib/virginity/vcard/field_values/optional_structured_text.rb +35 -0
  28. data/lib/virginity/vcard/field_values/separated_text.rb +59 -0
  29. data/lib/virginity/vcard/field_values/structured_text.rb +71 -0
  30. data/lib/virginity/vcard/field_values/text.rb +23 -0
  31. data/lib/virginity/vcard/field_values/uri.rb +15 -0
  32. data/lib/virginity/vcard/fields.rb +284 -0
  33. data/lib/virginity/vcard/fields_osx.rb +95 -0
  34. data/lib/virginity/vcard/fields_soocial.rb +45 -0
  35. data/lib/virginity/vcard/name_handler.rb +151 -0
  36. data/lib/virginity/vcard/patching.rb +262 -0
  37. data/lib/virginity/vcard21.rb +2 -0
  38. data/lib/virginity/vcard21/base.rb +30 -0
  39. data/lib/virginity/vcard21/parser.rb +359 -0
  40. data/lib/virginity/vcard21/reader.rb +103 -0
  41. data/lib/virginity/vcard21/writer.rb +139 -0
  42. metadata +111 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: d5ded8c5c157bd494a6ea4644503ec864cdc4e32
4
+ data.tar.gz: 3b6e0534423dda14a021437c9ca323ef47dd5814
5
+ SHA512:
6
+ metadata.gz: f4b0415cde8eac73ea8af7c6b67fbbcec313a034a0b895a00df27e5b5516bdbf2e0b721a89b559461c6100395dc968464e1e4b4bee89bb3b66653dd0b96f226d
7
+ data.tar.gz: 3fe5472499124c8bd04e6647e91d86ef29d8152e9f47f301c8fa622be9c5f07fae01e87be91e891e67c79dda9d25b84ab75f0fdbf1881c3901d848d5b56314cd
data/lib/virginity.rb ADDED
@@ -0,0 +1,6 @@
1
+ # V stands for virginity according to http://www.urbandictionary.com/define.php?term=V%20card
2
+
3
+ require 'active_support/core_ext/string/multibyte.rb'
4
+ require 'virginity/vcard'
5
+ require 'virginity/fixes'
6
+ require 'virginity/vcard/patching'
@@ -0,0 +1,87 @@
1
+ require 'virginity'
2
+ require 'digest/sha1'
3
+
4
+
5
+ # FIXME move this to the API
6
+ module Virginity
7
+ class ContentLine
8
+
9
+ # api_id, a SHA1 hash of the whole line
10
+ def api_id
11
+ Digest::SHA1.hexdigest(to_s)
12
+ end
13
+ end
14
+ end
15
+
16
+
17
+ require 'virginity/api_extensions/fields_to_xml'
18
+
19
+
20
+ module Virginity
21
+ class Param
22
+ def as_json(options = {})
23
+ { key => value }
24
+ end
25
+ end
26
+
27
+ module StructuredTextAsJson
28
+ def as_json(options = {})
29
+ hash = {}
30
+ components.each do |c|
31
+ hash[c.to_s] = send(c)
32
+ end
33
+ field_as_json(hash, options)
34
+ end
35
+ end
36
+
37
+ module SeparatedTextAsJson
38
+ def as_json(options = {})
39
+ field_as_json({ :values => [values] }, options)
40
+ end
41
+ end
42
+
43
+ module Values
44
+ module Binary
45
+ def as_json(options = {})
46
+ field_as_json({ :binary => binary }, options)
47
+ end
48
+ end
49
+ end
50
+
51
+ class BaseField < ContentLine
52
+ def group_and_params_as_json(options = {})
53
+ { :group => group, :params => params }
54
+ end
55
+
56
+ def field_as_json(hash, options = {})
57
+ group_and_params_as_json(options).merge(hash).delete_if { |k,v| v.nil? }
58
+ end
59
+ end
60
+
61
+
62
+
63
+ class Email < BaseField
64
+ def as_json(options = {})
65
+ field_as_json({ :address => address }, options)
66
+ end
67
+ end
68
+
69
+ class Tel < BaseField
70
+ def as_json(options = {})
71
+ field_as_json({ :number => number }, options)
72
+ end
73
+ end
74
+
75
+ class Adr < BaseField
76
+ include StructuredTextAsJson
77
+ end
78
+
79
+ class Name < BaseField
80
+ include StructuredTextAsJson
81
+ end
82
+
83
+ class Separated < BaseField
84
+ include SeparatedTextAsJson
85
+ end
86
+
87
+ end
@@ -0,0 +1,82 @@
1
+ module Virginity
2
+
3
+ class Email < BaseField
4
+ def as_json(options = {})
5
+ { :email => {
6
+ :id => api_id,
7
+ :params => params,
8
+ :address => address
9
+ }}
10
+ end
11
+ end
12
+
13
+
14
+ class Tel < BaseField
15
+ def as_json(options = {})
16
+ { :tel => {
17
+ :id => api_id,
18
+ :params => params,
19
+ :number => number
20
+ }}
21
+ end
22
+ end
23
+
24
+
25
+ class Url < BaseField
26
+ def as_json(options = {})
27
+ { :url => {
28
+ :id => api_id,
29
+ :text => text
30
+ }}
31
+ end
32
+ end
33
+
34
+
35
+ class Adr < BaseField
36
+ def as_json(options = {})
37
+ hash = { :adr => {
38
+ :id => api_id,
39
+ :params => params,
40
+ }}
41
+ components.each do |component|
42
+ hash[component] = send(component))
43
+ end
44
+ hash
45
+ end
46
+ end
47
+
48
+
49
+ class Org < BaseField
50
+ def as_json(options = {})
51
+ { :org => {
52
+ :id => api_id,
53
+ :name => orgname,
54
+ :unit1 => unit1,
55
+ :unit2 => unit2
56
+ }}
57
+ end
58
+ end
59
+
60
+
61
+ class Impp < BaseField
62
+ def as_json(options = {})
63
+ { :impp => {
64
+ :id => api_id,
65
+ :scheme => scheme,
66
+ :address => address,
67
+ :text => text
68
+ }}
69
+ end
70
+ end
71
+
72
+
73
+ class Note < BaseField
74
+ def as_json(options = {})
75
+ { :note => {
76
+ :id => api_id,
77
+ :text => text
78
+ }}
79
+ end
80
+ end
81
+
82
+ end
@@ -0,0 +1,151 @@
1
+ module Virginity
2
+
3
+
4
+ class BaseField < ContentLine
5
+ def params_to_xml!(params, builder)
6
+ builder.params(:type => "array") do
7
+ params.each do |p|
8
+ builder.tag!(p.key, p.value, :type => "string")
9
+ end
10
+ end
11
+ end
12
+
13
+ def extra_fields_to_xml(fields, builder)
14
+ fields.each_pair { |k,v| builder.tag!(k, v) } unless fields.nil?
15
+ end
16
+ end
17
+
18
+
19
+ class BaseField < ContentLine
20
+ def params_to_xml
21
+ s = ""
22
+ unless params.empty?
23
+ s << "<params>"
24
+ params.each do |p|
25
+ s << xml_element(p.key, p.value)
26
+ end
27
+ s << "</params>"
28
+ end
29
+ s
30
+ end
31
+
32
+ # def params_to_xml
33
+ # return "" if params.empty?
34
+ # "<params>#{params.map {|p| xml_element(p.key, p.value) }.join}</params>"
35
+ # end
36
+
37
+ def value_to_xml
38
+ xml_element(@value, @value.strip)
39
+ end
40
+
41
+ def to_xml
42
+ s = "<#{name.downcase}>"
43
+ s << xml_element("group", group) unless group.nil?
44
+ s << params_to_xml
45
+ s << value_to_xml
46
+ s << "</#{name.downcase}>"
47
+ end
48
+ end
49
+
50
+
51
+ class Email < BaseField
52
+ def to_xml(options = {})
53
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
54
+ xml.email(:index => api_id ) do
55
+ xml.id api_id, :type => "string"
56
+ # params_to_xml!(params, xml)
57
+ xml.address address
58
+ extra_fields_to_xml(options[:include], xml)
59
+ end
60
+ xml.target!
61
+ end
62
+ end
63
+
64
+
65
+ class Tel < BaseField
66
+ def to_xml(options = {})
67
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
68
+ xml.telephone(:index => api_id ) do
69
+ xml.id api_id, :type => "string"
70
+ # params_to_xml!(params, xml)
71
+ xml.number number
72
+ extra_fields_to_xml(options[:include], xml)
73
+ end
74
+ xml.target!
75
+ end
76
+ end
77
+
78
+
79
+ class Url < BaseField
80
+ def to_xml(options = {})
81
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
82
+ xml.url(:index => api_id) do
83
+ xml.id api_id, :type => "string"
84
+ xml.text text
85
+ extra_fields_to_xml(options[:include], xml)
86
+ end
87
+ xml.target!
88
+ end
89
+ end
90
+
91
+
92
+ class Adr < BaseField
93
+ def to_xml(options = {})
94
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
95
+ xml.address(:index => api_id) do
96
+ xml.id api_id, :type => "string"
97
+ # params_to_xml!(params, xml)
98
+ components.each do |component|
99
+ xml.tag!(component, send(component))
100
+ end
101
+ extra_fields_to_xml(options[:include], xml)
102
+ end
103
+ xml.target!
104
+ end
105
+ end
106
+
107
+
108
+ class Org < BaseField
109
+ def to_xml(options = {})
110
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
111
+ xml.organisation :index => api_id do
112
+ xml.id api_id, :type => "string"
113
+ xml.name orgname
114
+ xml.unit1 unit1
115
+ xml.unit2 unit2
116
+ extra_fields_to_xml(options[:include], xml)
117
+ end
118
+ xml.target!
119
+ end
120
+ end
121
+
122
+
123
+ class Impp < BaseField
124
+ def to_xml(options = {})
125
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
126
+ xml.impp(:index => api_id ) do
127
+ xml.id api_id, :type => "string"
128
+ xml.scheme scheme
129
+ xml.address address
130
+ xml.value text
131
+ extra_fields_to_xml(options[:include], xml)
132
+ end
133
+ xml.target!
134
+ end
135
+ end
136
+
137
+
138
+ class TextField < BaseField
139
+ def to_xml(options = {})
140
+ xml = options[:builder] || Builder::XmlMarkup.new(options)
141
+ xml.note(:index => api_id) do
142
+ xml.id api_id, :type => "string"
143
+ xml.text text
144
+ extra_fields_to_xml(options[:include], xml)
145
+ end
146
+ xml.target!
147
+ end
148
+ end
149
+
150
+
151
+ end
@@ -0,0 +1,84 @@
1
+ module Virginity
2
+
3
+ module Rfc882
4
+ # rfc822 has a slightly different definition of quoted string compared to rfc2425... it's better
5
+ QTEXT = /[^\"\\\n]/ # <any CHAR excepting <"> "\" & CR, and including linear-white-space
6
+ QUOTED_PAIR = /\\./
7
+ QUOTED_STRING = /(\"((#{QTEXT}|#{QUOTED_PAIR})*)\")/
8
+ end
9
+
10
+
11
+ module Rfc2234 #:nodoc: # Augmented BNF for Syntax Specifications: ABNF
12
+ CRLF = /\r\n/
13
+ DIGIT = /\d/
14
+ WSP = /\s/ # whitespace
15
+ end
16
+
17
+ module Rfc2425
18
+ end
19
+
20
+ # Contains regular expression strings for the EBNF of rfc 2425.
21
+ module Bnf #:nodoc:
22
+ include Rfc882
23
+ include Rfc2234
24
+ include Rfc2425
25
+ # 1*(ALPHA / DIGIT / "-")
26
+ # added underscore '_' because it's produced by Notes - X-LOTUS-CHILD_UID
27
+ # added a slash '/' so that it will match lines like: "X-messaging/xmpp-All:someone@gmail.com"
28
+ # added a space ' ' so that it will match lines like: "X-GOOGLE TALK;TYPE=WORK:janklaassen"
29
+ NAME = '[a-zA-Z0-9][a-zA-Z0-9\-\_\/\ ]*'
30
+
31
+ # <"> <Any character except CTLs, DQUOTE> <">
32
+ # CTL = <any ASCII control ; ( 0- 37, 0.- 31.)
33
+ # character and DEL> ; ( 177, 127.)
34
+ # DQOUTE = '"'
35
+ # QSTR = '"([^"]*)"'
36
+ QSTR = QUOTED_STRING # use the definition from rfc822
37
+
38
+ # *<Any character except CTLs, DQUOTE, ";", ":", ",">
39
+ PTEXT = '([^";:,]+)'
40
+
41
+ # param-value = ptext / quoted-string
42
+ PVALUE = "(?:#{QSTR}|#{PTEXT})"
43
+
44
+ # param = name "=" param-value *("," param-value)
45
+ PARAM = ";(#{NAME})=((?:#{PVALUE})?(?:,#{PVALUE})*)"
46
+
47
+ # V3.0: contentline = [group "."] name *(";" param) ":" value
48
+ # V2.1: contentline = *( group "." ) name *(";" param) ":" value
49
+ #
50
+ #LINE = "((?:#{NAME}\\.)*)?(#{NAME})([^:]*)\:(.*)"
51
+ # tcmalloc (used by ree) having memory issues with that one:
52
+ #LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):(.*)$"
53
+ #LINE = "^((?:#{NAME}\\.)*)?(#{NAME})((?:#{PARAM})*):"
54
+ # We do not accept the V2.1 syntax.
55
+ LINE = "^(#{NAME}\\.)?(#{NAME})((?:#{PARAM})*):"
56
+
57
+ # date = date-fullyear ["-"] date-month ["-"] date-mday
58
+ # date-fullyear = 4 DIGIT
59
+ # date-month = 2 DIGIT
60
+ # date-mday = 2 DIGIT
61
+ DATE = '(\d\d\d\d)-?(\d\d)-?(\d\d)'
62
+
63
+ # time = time-hour [":"] time-minute [":"] time-second [time-secfrac] [time-zone]
64
+ # time-hour = 2 DIGIT
65
+ # time-minute = 2 DIGIT
66
+ # time-second = 2 DIGIT
67
+ # time-secfrac = "," 1*DIGIT
68
+ # time-zone = "Z" / time-numzone
69
+ # time-numzome = sign time-hour [":"] time-minute
70
+ TIME = '(\d\d):?(\d\d):?(\d\d)(\.\d+)?(Z|[-+]\d\d:?\d\d)?'
71
+
72
+ # integer = (["+"] / "-") 1*DIGIT
73
+ INTEGER = '[-+]?\d+'
74
+
75
+ # QSAFE-CHAR = WSP / %x21 / %x23-7E / NON-US-ASCII
76
+ # ; Any character except CTLs and DQUOTE
77
+ QSAFECHAR = '[ \t\x21\x23-\x7e\x80-\xff]'
78
+
79
+ # SAFE-CHAR = WSP / %x21 / %x23-2B / %x2D-39 / %x3C-7E / NON-US-ASCII
80
+ # ; Any character except CTLs, DQUOTE, ";", ":", ","
81
+ SAFECHAR = '[ \t\x21\x23-\x2b\x2d-\x39\x3c-\x7e\x80-\xff]'
82
+ end
83
+
84
+ end
@@ -0,0 +1,93 @@
1
+ require 'virginity/bnf'
2
+ require 'virginity/dir_info/line_folding'
3
+ require 'virginity/dir_info/content_line'
4
+ require 'virginity/dir_info/query'
5
+
6
+ require 'virginity/vcard21/reader'
7
+
8
+ $KCODE = 'U' unless defined? Encoding::UTF_8
9
+
10
+ module Virginity
11
+
12
+ # see rfc 2425, MIME Content-Type for Directory Information.
13
+ #
14
+ # Basically a DirectoryInformation-object is a collection of lines (see ContentLine)
15
+ class DirectoryInformation
16
+ include Query
17
+ extend Virginity::Vcard21::Reader
18
+ extend Encodings
19
+ attr_reader :lines
20
+
21
+ # decode directory information text
22
+ # TODO accept an array of lines as the argument, make a special from_string(string="")
23
+ def initialize(string = "")
24
+ raise "expected a string but found #{string.inspect}, a #{string.class}" unless string.is_a? String
25
+ @lines = LineFolding::unfold_and_split(string).map do |line|
26
+ ContentLine.parse(line)
27
+ end
28
+ end
29
+
30
+ # string representation
31
+ def encode
32
+ LineFolding::fold(unfolded)
33
+ end
34
+ alias_method :to_s, :encode
35
+
36
+ # replace _all_ lines
37
+ def lines=(replace_lines)
38
+ @lines = replace_lines.to_a.map { |line| ContentLine.parse(line.to_s) }
39
+ end
40
+
41
+ def inspect
42
+ "#<#{self.class}:#{object_id}>"
43
+ end
44
+
45
+ def delete(*lines_to_delete)
46
+ lines_to_delete.compact.map { |line| lines.delete(line) }.compact
47
+ end
48
+
49
+ # remove only this exact content line identified by its object-id
50
+ def delete_content_line(cl)
51
+ lines.delete_if { |line| line.object_id == cl.object_id }
52
+ end
53
+
54
+ # append a line
55
+ def <<(line)
56
+ lines << ContentLine.parse(line.to_s)
57
+ end
58
+ alias_method :push, :<<
59
+
60
+ # string representation that is not folded (see LineFolding)
61
+ LF = "\n"
62
+ def unfolded
63
+ @lines.join(LF) << LF
64
+ end
65
+
66
+ def pretty_print(q)
67
+ q.text unfolded
68
+ end
69
+
70
+ # equallity is defined as having the same lines
71
+ def ==(other)
72
+ return false if other.class != self.class
73
+ # checking for lines.size is an optimisation
74
+ @lines.size == other.lines.size and @lines.sort == other.lines.sort
75
+ end
76
+
77
+ # eql? is defined as being of the same class (not a descendent class like Vcard) and having the same lines
78
+ def eql?(other)
79
+ self.class == other.class and self == other
80
+ end
81
+
82
+ # are all @lines also present in other?
83
+ def subset_of?(other)
84
+ @lines.all? do |line|
85
+ other.first_match(line.to_s)
86
+ end
87
+ end
88
+
89
+ def superset_of?(other)
90
+ other.subset_of?(self)
91
+ end
92
+ end
93
+ end