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.
- checksums.yaml +7 -0
- data/lib/virginity.rb +6 -0
- data/lib/virginity/api_extensions.rb +87 -0
- data/lib/virginity/api_extensions/fields_to_json.rb +82 -0
- data/lib/virginity/api_extensions/fields_to_xml.rb +151 -0
- data/lib/virginity/bnf.rb +84 -0
- data/lib/virginity/dir_info.rb +93 -0
- data/lib/virginity/dir_info/content_line.rb +146 -0
- data/lib/virginity/dir_info/line_folding.rb +60 -0
- data/lib/virginity/dir_info/param.rb +208 -0
- data/lib/virginity/dir_info/query.rb +144 -0
- data/lib/virginity/encoding_decoding.rb +177 -0
- data/lib/virginity/encodings.rb +36 -0
- data/lib/virginity/fixes.rb +230 -0
- data/lib/virginity/vcard.rb +244 -0
- data/lib/virginity/vcard/base_field.rb +126 -0
- data/lib/virginity/vcard/categories.rb +57 -0
- data/lib/virginity/vcard/cleaning.rb +364 -0
- data/lib/virginity/vcard/field.rb +22 -0
- data/lib/virginity/vcard/field/params.rb +93 -0
- data/lib/virginity/vcard/field_values.rb +10 -0
- data/lib/virginity/vcard/field_values/binary.rb +22 -0
- data/lib/virginity/vcard/field_values/boolean.rb +14 -0
- data/lib/virginity/vcard/field_values/case_insensitive_value.rb +13 -0
- data/lib/virginity/vcard/field_values/date.rb +16 -0
- data/lib/virginity/vcard/field_values/integer.rb +15 -0
- data/lib/virginity/vcard/field_values/optional_structured_text.rb +35 -0
- data/lib/virginity/vcard/field_values/separated_text.rb +59 -0
- data/lib/virginity/vcard/field_values/structured_text.rb +71 -0
- data/lib/virginity/vcard/field_values/text.rb +23 -0
- data/lib/virginity/vcard/field_values/uri.rb +15 -0
- data/lib/virginity/vcard/fields.rb +284 -0
- data/lib/virginity/vcard/fields_osx.rb +95 -0
- data/lib/virginity/vcard/fields_soocial.rb +45 -0
- data/lib/virginity/vcard/name_handler.rb +151 -0
- data/lib/virginity/vcard/patching.rb +262 -0
- data/lib/virginity/vcard21.rb +2 -0
- data/lib/virginity/vcard21/base.rb +30 -0
- data/lib/virginity/vcard21/parser.rb +359 -0
- data/lib/virginity/vcard21/reader.rb +103 -0
- data/lib/virginity/vcard21/writer.rb +139 -0
- 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,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
|