plist_lite 1.1.0
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/ext/plist_lite/ext/ext.c +233 -0
- data/ext/plist_lite/ext/extconf.rb +4 -0
- data/lib/minimal.plist +1 -0
- data/lib/plist.dtd +19 -0
- data/lib/plist_lite.rb +100 -0
- data/lib/plist_lite/ext.bundle +0 -0
- metadata +120 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 81c314a0ff465217564b4040a14bfc7a8913e26d9be6b365c489f4117f07fd76
|
4
|
+
data.tar.gz: 0ae9856ea9b8b13c691a4459ea907140d32d99cafedc6b726c570649a4ea77f3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c12c104cfd7c1daf21fc7813e7dacc6498ec14efce8e3950dafae565050ee6b6fa9488638a4fe9e254cdcfb4388776005ab22c6c3495ad8cc4e1f155212c3bc6
|
7
|
+
data.tar.gz: 49ef1de68941d7683ec69843dc9a607ca97b85de8f9b2a7fbf299ffd66d2f965ce445593405c492ef54f3a2d7ed080888ac8eb8d6a6d7522702607a4b06400e8
|
@@ -0,0 +1,233 @@
|
|
1
|
+
#include "ruby.h"
|
2
|
+
#include "ruby/encoding.h"
|
3
|
+
#include <stdint.h>
|
4
|
+
#include <stdlib.h>
|
5
|
+
|
6
|
+
typedef struct
|
7
|
+
{
|
8
|
+
char *head;
|
9
|
+
size_t size;
|
10
|
+
size_t length;
|
11
|
+
} string;
|
12
|
+
|
13
|
+
string *string_new(size_t size)
|
14
|
+
{
|
15
|
+
string *ptr = malloc(sizeof(string));
|
16
|
+
ptr->size = size;
|
17
|
+
ptr->head = malloc(size);
|
18
|
+
ptr->head[0] = '\0';
|
19
|
+
ptr->length = 0;
|
20
|
+
return ptr;
|
21
|
+
}
|
22
|
+
|
23
|
+
void string_concat(string *recv, char *src)
|
24
|
+
{
|
25
|
+
size_t len_src = strlen(src);
|
26
|
+
size_t new_size = recv->size;
|
27
|
+
while ((recv->length + len_src + 1) > new_size)
|
28
|
+
new_size <<= 1;
|
29
|
+
if (new_size > recv->size)
|
30
|
+
recv->head = realloc(recv->head, new_size);
|
31
|
+
|
32
|
+
char *ptr = recv->head + recv->length;
|
33
|
+
while ((*ptr++ = *src++))
|
34
|
+
;
|
35
|
+
recv->length += len_src;
|
36
|
+
}
|
37
|
+
|
38
|
+
void string_free(string *recv)
|
39
|
+
{
|
40
|
+
free(recv->head);
|
41
|
+
free(recv);
|
42
|
+
}
|
43
|
+
|
44
|
+
void time_to_plist_date(VALUE time, string *output)
|
45
|
+
{
|
46
|
+
rb_funcall(time, rb_intern("utc"), 0);
|
47
|
+
VALUE str = rb_funcall(time, rb_intern("iso8601"), 0);
|
48
|
+
string_concat(output, (char *)"<date>");
|
49
|
+
string_concat(output, StringValueCStr(str));
|
50
|
+
string_concat(output, (char *)"</date>");
|
51
|
+
}
|
52
|
+
|
53
|
+
void dump_node(VALUE obj, string *output);
|
54
|
+
|
55
|
+
VALUE to_utf8_xml_text(VALUE obj)
|
56
|
+
{
|
57
|
+
int idx = rb_enc_get_index(obj);
|
58
|
+
if (idx < 0)
|
59
|
+
rb_raise(rb_eTypeError, "unknown encoding");
|
60
|
+
if (idx == rb_utf8_encindex())
|
61
|
+
{
|
62
|
+
VALUE options = rb_hash_new();
|
63
|
+
rb_hash_aset(options, ID2SYM(rb_intern("xml")), ID2SYM(rb_intern("text")));
|
64
|
+
return rb_funcallv_kw(obj, rb_intern("encode"), 1, &options, RB_PASS_KEYWORDS);
|
65
|
+
}
|
66
|
+
else
|
67
|
+
{
|
68
|
+
VALUE options = rb_hash_new();
|
69
|
+
rb_hash_aset(options, ID2SYM(rb_intern("xml")), ID2SYM(rb_intern("text")));
|
70
|
+
VALUE args[] = {rb_enc_from_encoding(rb_utf8_encoding()), options};
|
71
|
+
return rb_funcallv_kw(obj, rb_intern("encode"), 2, args, RB_PASS_KEYWORDS);
|
72
|
+
}
|
73
|
+
}
|
74
|
+
|
75
|
+
VALUE array_each_block(VALUE obj, string *output)
|
76
|
+
{
|
77
|
+
dump_node(obj, output);
|
78
|
+
return Qnil;
|
79
|
+
}
|
80
|
+
|
81
|
+
VALUE hash_each_block(VALUE pair, string *output)
|
82
|
+
{
|
83
|
+
VALUE value = rb_ary_pop(pair);
|
84
|
+
VALUE key = rb_ary_pop(pair);
|
85
|
+
string_concat(output, (char *)"<key>");
|
86
|
+
switch (TYPE(key))
|
87
|
+
{
|
88
|
+
case T_STRING:
|
89
|
+
{
|
90
|
+
VALUE encoded = to_utf8_xml_text(key);
|
91
|
+
string_concat(output, StringValueCStr(encoded));
|
92
|
+
break;
|
93
|
+
}
|
94
|
+
case T_SYMBOL:
|
95
|
+
{
|
96
|
+
VALUE str = rb_funcall(key, rb_intern("to_s"), 0);
|
97
|
+
string_concat(output, StringValueCStr(str));
|
98
|
+
break;
|
99
|
+
}
|
100
|
+
default:
|
101
|
+
{
|
102
|
+
VALUE encoded = to_utf8_xml_text(rb_funcall(key, rb_intern("to_s"), 0));
|
103
|
+
string_concat(output, StringValueCStr(encoded));
|
104
|
+
break;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
string_concat(output, (char *)"</key>");
|
108
|
+
dump_node(value, output);
|
109
|
+
return Qnil;
|
110
|
+
}
|
111
|
+
|
112
|
+
void dump_node(VALUE obj, string *output)
|
113
|
+
{
|
114
|
+
switch (TYPE(obj))
|
115
|
+
{
|
116
|
+
case T_ARRAY:
|
117
|
+
string_concat(output, (char *)"<array>");
|
118
|
+
rb_block_call(obj, rb_intern("each"), 0, NULL, (rb_block_call_func_t)array_each_block, (VALUE)output);
|
119
|
+
string_concat(output, (char *)"</array>");
|
120
|
+
break;
|
121
|
+
case T_HASH:
|
122
|
+
string_concat(output, (char *)"<dict>");
|
123
|
+
rb_block_call(obj, rb_intern("each"), 0, NULL, (rb_block_call_func_t)hash_each_block, (VALUE)output);
|
124
|
+
string_concat(output, (char *)"</dict>");
|
125
|
+
break;
|
126
|
+
case T_TRUE:
|
127
|
+
string_concat(output, (char *)"<true/>");
|
128
|
+
break;
|
129
|
+
case T_FALSE:
|
130
|
+
{
|
131
|
+
string_concat(output, (char *)"<false/>");
|
132
|
+
break;
|
133
|
+
}
|
134
|
+
case T_BIGNUM:
|
135
|
+
case T_FIXNUM:
|
136
|
+
{
|
137
|
+
VALUE str = rb_funcall(obj, rb_intern("to_s"), 0);
|
138
|
+
string_concat(output, (char *)"<integer>");
|
139
|
+
string_concat(output, StringValueCStr(str));
|
140
|
+
string_concat(output, (char *)"</integer>");
|
141
|
+
break;
|
142
|
+
}
|
143
|
+
case T_FLOAT:
|
144
|
+
{
|
145
|
+
VALUE str = rb_funcall(obj, rb_intern("to_s"), 0);
|
146
|
+
string_concat(output, (char *)"<real>");
|
147
|
+
string_concat(output, StringValueCStr(str));
|
148
|
+
string_concat(output, (char *)"</real>");
|
149
|
+
break;
|
150
|
+
}
|
151
|
+
case T_STRING:
|
152
|
+
{
|
153
|
+
int idx = rb_enc_get_index(obj);
|
154
|
+
if (idx == rb_ascii8bit_encindex())
|
155
|
+
{
|
156
|
+
string_concat(output, (char *)"<data>");
|
157
|
+
VALUE data = rb_funcall(
|
158
|
+
rb_ary_new_from_values(1, &obj),
|
159
|
+
rb_intern("pack"),
|
160
|
+
1,
|
161
|
+
rb_str_new_literal("m"));
|
162
|
+
string_concat(output, StringValueCStr(data));
|
163
|
+
string_concat(output, (char *)"</data>");
|
164
|
+
}
|
165
|
+
else
|
166
|
+
{
|
167
|
+
string_concat(output, (char *)"<string>");
|
168
|
+
VALUE encoded = to_utf8_xml_text(obj);
|
169
|
+
string_concat(output, StringValueCStr(encoded));
|
170
|
+
string_concat(output, (char *)"</string>");
|
171
|
+
}
|
172
|
+
|
173
|
+
break;
|
174
|
+
}
|
175
|
+
case T_SYMBOL:
|
176
|
+
{
|
177
|
+
VALUE str = rb_funcall(obj, rb_intern("to_s"), 0);
|
178
|
+
string_concat(output, (char *)"<string>");
|
179
|
+
string_concat(output, StringValueCStr(str));
|
180
|
+
string_concat(output, (char *)"</string>");
|
181
|
+
break;
|
182
|
+
}
|
183
|
+
default:
|
184
|
+
{
|
185
|
+
if (rb_obj_is_kind_of(obj, rb_cTime) == Qtrue)
|
186
|
+
{
|
187
|
+
VALUE time = rb_funcall(rb_cTime, rb_intern("at"), 1, obj);
|
188
|
+
time_to_plist_date(time, output);
|
189
|
+
}
|
190
|
+
else if (rb_obj_is_kind_of(obj, rb_const_get(rb_cObject, rb_intern("DateTime"))) == Qtrue)
|
191
|
+
{
|
192
|
+
VALUE time = rb_funcall(obj, rb_intern("to_time"), 0);
|
193
|
+
time_to_plist_date(time, output);
|
194
|
+
}
|
195
|
+
else if (rb_obj_is_kind_of(obj, rb_const_get(rb_cObject, rb_intern("Date"))) == Qtrue)
|
196
|
+
{
|
197
|
+
rb_warn("Consider not using Date object because it does not contain time zone information");
|
198
|
+
VALUE str = rb_funcall(obj, rb_intern("iso8601"), 0);
|
199
|
+
string_concat(output, (char *)"<date>");
|
200
|
+
string_concat(output, StringValueCStr(str));
|
201
|
+
string_concat(output, (char *)"T00:00:00Z</date>");
|
202
|
+
}
|
203
|
+
else
|
204
|
+
{
|
205
|
+
VALUE msg = rb_funcall(rb_obj_class(obj), rb_intern("to_s"), 0);
|
206
|
+
rb_raise(rb_eArgError, "Unsupported type: %s", StringValueCStr(msg));
|
207
|
+
}
|
208
|
+
break;
|
209
|
+
}
|
210
|
+
}
|
211
|
+
}
|
212
|
+
|
213
|
+
VALUE dump(VALUE recv, VALUE obj)
|
214
|
+
{
|
215
|
+
string *output = string_new(2048);
|
216
|
+
string_concat(
|
217
|
+
output,
|
218
|
+
(char *)"<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
219
|
+
"<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">"
|
220
|
+
"<plist version=\"1.0\">");
|
221
|
+
dump_node(obj, output);
|
222
|
+
string_concat(output, (char *)"</plist>");
|
223
|
+
VALUE ret = rb_utf8_str_new(output->head, output->length);
|
224
|
+
string_free(output);
|
225
|
+
return ret;
|
226
|
+
}
|
227
|
+
|
228
|
+
void Init_ext(void)
|
229
|
+
{
|
230
|
+
rb_require("time");
|
231
|
+
VALUE cPlistLite = rb_define_module("PlistLite");
|
232
|
+
rb_define_singleton_method(cPlistLite, "dump", dump, 1);
|
233
|
+
}
|
data/lib/minimal.plist
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0"?><!DOCTYPE plist SYSTEM "plist.dtd"><_/>
|
data/lib/plist.dtd
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
<!ENTITY % plistObject "(array | data | date | dict | real | integer | string | true | false )" >
|
2
|
+
<!ELEMENT plist %plistObject;>
|
3
|
+
<!ATTLIST plist version CDATA "1.0" >
|
4
|
+
|
5
|
+
<!-- Collections -->
|
6
|
+
<!ELEMENT array (%plistObject;)*>
|
7
|
+
<!ELEMENT dict (key, %plistObject;)*>
|
8
|
+
<!ELEMENT key (#PCDATA)>
|
9
|
+
|
10
|
+
<!--- Primitive types -->
|
11
|
+
<!ELEMENT string (#PCDATA)>
|
12
|
+
<!ELEMENT data (#PCDATA)> <!-- Contents interpreted as Base-64 encoded -->
|
13
|
+
<!ELEMENT date (#PCDATA)> <!-- Contents should conform to a subset of ISO 8601 (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'. Smaller units may be omitted with a loss of precision) -->
|
14
|
+
|
15
|
+
<!-- Numerical primitives -->
|
16
|
+
<!ELEMENT true EMPTY> <!-- Boolean constant true -->
|
17
|
+
<!ELEMENT false EMPTY> <!-- Boolean constant false -->
|
18
|
+
<!ELEMENT real (#PCDATA)> <!-- Contents should represent a floating point number matching ("+" | "-")? d+ ("."d*)? ("E" ("+" | "-") d+)? where d is a digit 0-9. -->
|
19
|
+
<!ELEMENT integer (#PCDATA)> <!-- Contents should represent a (possibly signed) integer number in base 10 -->
|
data/lib/plist_lite.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'nokogiri'
|
4
|
+
require 'time'
|
5
|
+
module PlistLite
|
6
|
+
DTD = Dir.chdir(__dir__) do
|
7
|
+
Nokogiri::XML::Document.parse(
|
8
|
+
IO.read("#{__dir__}/minimal.plist"), nil, nil,
|
9
|
+
Nokogiri::XML::ParseOptions.new(Nokogiri::XML::ParseOptions::DTDLOAD)
|
10
|
+
)
|
11
|
+
end.external_subset
|
12
|
+
|
13
|
+
class << self
|
14
|
+
def load(source)
|
15
|
+
doc = Nokogiri::XML::Document.parse(
|
16
|
+
source, nil, nil,
|
17
|
+
Nokogiri::XML::ParseOptions.new(Nokogiri::XML::ParseOptions::STRICT)
|
18
|
+
)
|
19
|
+
raise doc.errors.first unless doc.errors.empty?
|
20
|
+
|
21
|
+
errors = DTD.validate(doc)
|
22
|
+
raise errors.first unless errors.empty?
|
23
|
+
|
24
|
+
load_node(doc.root.elements.first)
|
25
|
+
end
|
26
|
+
|
27
|
+
def dump(obj)
|
28
|
+
output = +'<?xml version="1.0" encoding="UTF-8"?>' \
|
29
|
+
'<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">' \
|
30
|
+
'<plist version="1.0">'
|
31
|
+
dump_node(obj, output)
|
32
|
+
output << '</plist>'
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def load_node(node)
|
38
|
+
case node.name
|
39
|
+
when 'dict'
|
40
|
+
hash = {}
|
41
|
+
node.elements.each_slice(2) do |key_node, value_node|
|
42
|
+
hash[key_node.text] = load_node(value_node)
|
43
|
+
end
|
44
|
+
hash
|
45
|
+
when 'array'
|
46
|
+
array = []
|
47
|
+
node.elements.each { |element| array << load_node(element) }
|
48
|
+
array
|
49
|
+
when 'integer' then node.text.to_i
|
50
|
+
when 'real' then node.text.to_f
|
51
|
+
when 'date' then Time.iso8601(node.text)
|
52
|
+
when 'string' then node.text
|
53
|
+
when 'data' then node.text.unpack1('m')
|
54
|
+
when 'true' then true
|
55
|
+
when 'false' then false
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def dump_node(obj, output)
|
60
|
+
case obj
|
61
|
+
when Hash
|
62
|
+
output << '<dict>'
|
63
|
+
obj.each do |key, value|
|
64
|
+
case key
|
65
|
+
when String then output << "<key>#{key.encode(xml: :text)}</key>"
|
66
|
+
when Symbol then output << "<key>#{key}</key>"
|
67
|
+
else
|
68
|
+
raise TypeError, 'Hash key should be String or Symbol'
|
69
|
+
end
|
70
|
+
dump_node(value, output)
|
71
|
+
end
|
72
|
+
output << '</dict>'
|
73
|
+
when Array
|
74
|
+
output << '<array>'
|
75
|
+
obj.each { |i| dump_node(i, output) }
|
76
|
+
output << '</array>'
|
77
|
+
when Symbol then output << "<string>#{obj}</string>"
|
78
|
+
when String
|
79
|
+
output <<
|
80
|
+
case obj.encoding
|
81
|
+
when Encoding::ASCII_8BIT then "<data>#{[obj].pack('m')}</data>"
|
82
|
+
when Encoding::UTF_8 then "<string>#{obj.encode(xml: :text)}</string>"
|
83
|
+
else "<string>#{obj.encode(Encoding::UTF_8, xml: :text)}</string>"
|
84
|
+
end
|
85
|
+
when Integer then output << "<integer>#{obj}</integer>"
|
86
|
+
when Float then output << "<real>#{obj}</real>"
|
87
|
+
when true then output << '<true/>'
|
88
|
+
when false then output << '<false/>'
|
89
|
+
when Time then output << "<date>#{Time.at(obj).utc.iso8601}</date>"
|
90
|
+
when DateTime then output << "<date>#{obj.to_time.utc.iso8601}</date>"
|
91
|
+
when Date
|
92
|
+
warn 'Consider not using Date object because it does not contain time zone information'
|
93
|
+
output << "<date>#{obj.iso8601}T00:00:00Z</date>"
|
94
|
+
else raise ArgumentError, "unknown type: #{obj.class}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
require 'plist_lite/ext'
|
Binary file
|
metadata
ADDED
@@ -0,0 +1,120 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: plist_lite
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Weihang Jian
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-06-27 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: nokogiri
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: bundler
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '2.0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '2.0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '5.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '13.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '13.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rake-compiler
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.0'
|
83
|
+
description: plist_lite is the fastest plist processor for Ruby written in C.It can
|
84
|
+
convert Ruby object to XML plist (a.k.a. property list), vice versa.
|
85
|
+
email: tonytonyjan@gmail.com
|
86
|
+
executables: []
|
87
|
+
extensions:
|
88
|
+
- ext/plist_lite/ext/extconf.rb
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ext/plist_lite/ext/ext.c
|
92
|
+
- ext/plist_lite/ext/extconf.rb
|
93
|
+
- lib/minimal.plist
|
94
|
+
- lib/plist.dtd
|
95
|
+
- lib/plist_lite.rb
|
96
|
+
- lib/plist_lite/ext.bundle
|
97
|
+
homepage: https://github.com/tonytonyjan/plist_lite
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ">="
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '2.7'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ">="
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubygems_version: 3.2.15
|
117
|
+
signing_key:
|
118
|
+
specification_version: 4
|
119
|
+
summary: plist_lite is the fastest plist processor for Ruby written in C.
|
120
|
+
test_files: []
|