tnetstrings 0.0.1
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/ext/tnetstrings.c +202 -0
- data/ext/tnetstrings.h +44 -0
- data/ext/tnetstrings.so +0 -0
- data/lib/tnetstrings.rb +108 -0
- data/lib/tnetstrings/version.rb +7 -0
- data/spec/tnetstrings_spec.rb +182 -0
- metadata +82 -0
data/ext/tnetstrings.c
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
#include "tnetstrings.h"
|
2
|
+
|
3
|
+
static TNETS_VALUE
|
4
|
+
eTnetsParserError;
|
5
|
+
|
6
|
+
#define TNETS_PARSER_ERROR(msg) rb_raise(eTnetsParserError, msg)
|
7
|
+
|
8
|
+
TNETS_VALUE tnets_render_number(char* payload, size_t len);
|
9
|
+
TNETS_VALUE tnets_render_string(char* payload, size_t len);
|
10
|
+
TNETS_VALUE tnets_render_bool (char* payload, size_t len);
|
11
|
+
TNETS_VALUE tnets_render_null (char* payload, size_t len);
|
12
|
+
TNETS_VALUE tnets_render_list (char* payload, size_t len);
|
13
|
+
TNETS_VALUE tnets_render_dict (char* payload, size_t len);
|
14
|
+
|
15
|
+
size_t tnets_size_spec(char* data, size_t len, char** payload);
|
16
|
+
|
17
|
+
TNETS_VALUE tnets_parse_dict_key(char* data, size_t len, char** remain);
|
18
|
+
TNETS_VALUE tnets_parse(char* data, size_t len, char** remain);
|
19
|
+
|
20
|
+
TNETS_VALUE rb_tnets_parse(TNETS_VALUE self, TNETS_VALUE rbstr);
|
21
|
+
|
22
|
+
// since no checking is done on strings, just pass it right along
|
23
|
+
// to the wrapper.
|
24
|
+
#define tnets_render_string TNETS_WRAP_STRING
|
25
|
+
|
26
|
+
TNETS_VALUE
|
27
|
+
tnets_render_number(char* payload, size_t len) {
|
28
|
+
size_t i;
|
29
|
+
int number = 0; // TODO: use a type that can handle
|
30
|
+
// larger numbers without wrapping.
|
31
|
+
unsigned short int negative = 0;
|
32
|
+
|
33
|
+
if (payload[0] == '-') {
|
34
|
+
negative = 1;
|
35
|
+
payload++;
|
36
|
+
len--;
|
37
|
+
}
|
38
|
+
|
39
|
+
for(i = 0; i < len; i++) {
|
40
|
+
if (IS_NUMERIC(payload[i])) {
|
41
|
+
number *= 10;
|
42
|
+
number += CTOI(payload[i]);
|
43
|
+
}
|
44
|
+
else {
|
45
|
+
TNETS_PARSER_ERROR("Non-numeric character in a tnets number");
|
46
|
+
}
|
47
|
+
}
|
48
|
+
|
49
|
+
if (negative) {
|
50
|
+
number *= -1;
|
51
|
+
}
|
52
|
+
|
53
|
+
return TNETS_WRAP_NUMBER(number);
|
54
|
+
}
|
55
|
+
|
56
|
+
TNETS_VALUE
|
57
|
+
tnets_render_bool(char* payload, size_t len) {
|
58
|
+
// TODO: do this better.
|
59
|
+
if (len != 4) return TNETS_WRAP_FALSE;
|
60
|
+
size_t i;
|
61
|
+
for (i = 0; i < len; i++) {
|
62
|
+
if (payload[i] != "true"[i]) {
|
63
|
+
return TNETS_WRAP_FALSE;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
return TNETS_WRAP_TRUE;
|
68
|
+
}
|
69
|
+
|
70
|
+
TNETS_VALUE
|
71
|
+
tnets_render_null(char* payload, size_t len) {
|
72
|
+
if (len != 0) TNETS_PARSER_ERROR("null must have an empty payload.");
|
73
|
+
|
74
|
+
return TNETS_WRAP_NULL;
|
75
|
+
}
|
76
|
+
|
77
|
+
TNETS_VALUE
|
78
|
+
tnets_render_list(char* payload, size_t len) {
|
79
|
+
char *tmp_payload = payload;
|
80
|
+
size_t sub_len;
|
81
|
+
TNETS_VALUE list = TNETS_NEW_LIST();
|
82
|
+
|
83
|
+
while (len > 0) {
|
84
|
+
TNETS_LIST_PUSH(
|
85
|
+
list,
|
86
|
+
tnets_parse(payload, len, &tmp_payload)
|
87
|
+
);
|
88
|
+
|
89
|
+
len -= (tmp_payload - payload);
|
90
|
+
payload = tmp_payload;
|
91
|
+
}
|
92
|
+
|
93
|
+
return list;
|
94
|
+
}
|
95
|
+
|
96
|
+
#define tnets_render_dict_key TNETS_WRAP_DICT_KEY
|
97
|
+
|
98
|
+
TNETS_VALUE
|
99
|
+
tnets_render_dict(char* payload, size_t len) {
|
100
|
+
char *tmp_payload = payload;
|
101
|
+
TNETS_VALUE dict = TNETS_NEW_DICT();
|
102
|
+
while(len > 0) {
|
103
|
+
TNETS_VALUE key = tnets_parse_dict_key(payload, len, &tmp_payload);
|
104
|
+
len -= (tmp_payload - payload);
|
105
|
+
payload = tmp_payload;
|
106
|
+
|
107
|
+
TNETS_DICT_SET(dict, key,
|
108
|
+
tnets_parse(payload, len, &tmp_payload)
|
109
|
+
);
|
110
|
+
|
111
|
+
len -= (tmp_payload - payload);
|
112
|
+
payload = tmp_payload;
|
113
|
+
}
|
114
|
+
|
115
|
+
return dict;
|
116
|
+
}
|
117
|
+
|
118
|
+
size_t
|
119
|
+
tnets_size_spec(char *data, size_t len, char **payload) {
|
120
|
+
size_t i;
|
121
|
+
size_t size = 0;
|
122
|
+
|
123
|
+
// only scan 11 characters (10 for length spec + 1 for colon)
|
124
|
+
size_t scan_limit = (len < 11) ? len : 11;
|
125
|
+
|
126
|
+
for (i = 0; i < scan_limit; i++) {
|
127
|
+
if (IS_NUMERIC(data[i])) {
|
128
|
+
size *= 10;
|
129
|
+
size += CTOI(data[i]);
|
130
|
+
}
|
131
|
+
else if (data[i] == ':') {
|
132
|
+
if (i + size > len + 1) {
|
133
|
+
TNETS_PARSER_ERROR("length spec longer than given string");
|
134
|
+
}
|
135
|
+
|
136
|
+
if (payload != NULL) {
|
137
|
+
*payload = data + i + 1; // add one to skip over the colon
|
138
|
+
}
|
139
|
+
return size;
|
140
|
+
}
|
141
|
+
else {
|
142
|
+
TNETS_PARSER_ERROR("non-numeric character in length spec");
|
143
|
+
}
|
144
|
+
}
|
145
|
+
|
146
|
+
TNETS_PARSER_ERROR("length spec more than 10 characters");
|
147
|
+
|
148
|
+
return 0;
|
149
|
+
}
|
150
|
+
|
151
|
+
TNETS_VALUE
|
152
|
+
tnets_parse_dict_key(char* data, size_t len, char** remain) {
|
153
|
+
char* payload;
|
154
|
+
size_t payload_len = tnets_size_spec(data, len, &payload);
|
155
|
+
if (remain != NULL) *remain = payload + payload_len + 1;
|
156
|
+
|
157
|
+
if (payload[payload_len] != tnets_tag_string) {
|
158
|
+
TNETS_PARSER_ERROR("non-string dict keys are not supported");
|
159
|
+
}
|
160
|
+
|
161
|
+
return tnets_render_dict_key(payload, payload_len);
|
162
|
+
}
|
163
|
+
|
164
|
+
TNETS_VALUE
|
165
|
+
tnets_parse(char *data, size_t len, char** remain) {
|
166
|
+
char *payload = NULL;
|
167
|
+
size_t payload_len;
|
168
|
+
tnets_type_tag type = tnets_tag_invalid;
|
169
|
+
|
170
|
+
payload_len = tnets_size_spec(data, len, &payload);
|
171
|
+
|
172
|
+
if (remain != NULL) *remain = payload + payload_len + 1;
|
173
|
+
|
174
|
+
type = payload[payload_len];
|
175
|
+
|
176
|
+
switch(type) {
|
177
|
+
case tnets_tag_string: return tnets_render_string(payload, payload_len);
|
178
|
+
case tnets_tag_number: return tnets_render_number(payload, payload_len);
|
179
|
+
case tnets_tag_null: return tnets_render_null(payload, payload_len);
|
180
|
+
case tnets_tag_bool: return tnets_render_bool(payload, payload_len);
|
181
|
+
case tnets_tag_list: return tnets_render_list(payload, payload_len);
|
182
|
+
case tnets_tag_dict: return tnets_render_dict(payload, payload_len);
|
183
|
+
default:
|
184
|
+
TNETS_PARSER_ERROR("Invalid type tag");
|
185
|
+
}
|
186
|
+
}
|
187
|
+
|
188
|
+
VALUE
|
189
|
+
rb_tnets_parse(VALUE self, VALUE rbstr) {
|
190
|
+
char* data = RSTRING_PTR(rbstr);
|
191
|
+
size_t len = RSTRING_LEN(rbstr);
|
192
|
+
return tnets_parse(data, len, NULL);
|
193
|
+
}
|
194
|
+
|
195
|
+
void
|
196
|
+
Init_tnetstrings() {
|
197
|
+
VALUE mTnetstrings = rb_define_module("TNETS");
|
198
|
+
|
199
|
+
rb_define_method(mTnetstrings, "c_parse", rb_tnets_parse, 1);
|
200
|
+
eTnetsParserError =
|
201
|
+
rb_define_class_under(mTnetstrings, "ParserError", rb_eIOError);
|
202
|
+
}
|
data/ext/tnetstrings.h
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
#include <ruby/ruby.h>
|
2
|
+
// #include <stdlib.h>
|
3
|
+
// #include <string.h>
|
4
|
+
#include <stdio.h>
|
5
|
+
#include <ruby/ruby.h>
|
6
|
+
|
7
|
+
typedef enum tnets_type_tag_e {
|
8
|
+
tnets_tag_string = ',',
|
9
|
+
tnets_tag_number = '#',
|
10
|
+
// tnets_tag_float = '^', // coming soon
|
11
|
+
tnets_tag_bool = '!',
|
12
|
+
tnets_tag_null = '~',
|
13
|
+
tnets_tag_dict = '}',
|
14
|
+
tnets_tag_list = ']',
|
15
|
+
tnets_tag_invalid = 'Z',
|
16
|
+
} tnets_type_tag;
|
17
|
+
|
18
|
+
#define IS_NUMERIC(ch) ('0' <= ch && ch <= '9')
|
19
|
+
#define CTOI(ch) (ch - '0');
|
20
|
+
|
21
|
+
#define TNETS_VALUE VALUE
|
22
|
+
|
23
|
+
#define TNETS_WRAP_STRING(s,l) (rb_str_new(s,l))
|
24
|
+
#define TNETS_WRAP_NUMBER INT2FIX
|
25
|
+
#define TNETS_WRAP_NULL Qnil
|
26
|
+
#define TNETS_WRAP_TRUE Qtrue
|
27
|
+
#define TNETS_WRAP_FALSE Qfalse
|
28
|
+
// TODO: find a better way to symbolize length-specified
|
29
|
+
// strings.
|
30
|
+
#define TNETS_WRAP_DICT_KEY(k,size) (ID2SYM(rb_to_id(TNETS_WRAP_STRING(k,size))))
|
31
|
+
|
32
|
+
#define TNETS_NEW_LIST rb_ary_new
|
33
|
+
#define TNETS_LIST_PUSH(a,v) rb_ary_push(a,v)
|
34
|
+
#define TNETS_NEW_DICT rb_hash_new
|
35
|
+
#define TNETS_DICT_SET(dict,key,val) (rb_hash_aset(dict,key,val))
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Parse an object off the front of a tnetstring.
|
39
|
+
* Returns a pointer to the parsed object, or NULL if an error occurs.
|
40
|
+
* The third argument is an output parameter; if non-NULL it will
|
41
|
+
* receive the unparsed remainder of the string.
|
42
|
+
*/
|
43
|
+
TNETS_VALUE
|
44
|
+
tnets_parse(char *data, size_t len, char** remain);
|
data/ext/tnetstrings.so
ADDED
Binary file
|
data/lib/tnetstrings.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
require File.expand_path('../ext/tnetstrings', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
module TNETS
|
4
|
+
extend self
|
5
|
+
|
6
|
+
def load(str)
|
7
|
+
c_parse(str)
|
8
|
+
end
|
9
|
+
|
10
|
+
def dump(obj, *a)
|
11
|
+
obj.to_tnets(*a)
|
12
|
+
end
|
13
|
+
|
14
|
+
class StreamParser
|
15
|
+
attr_reader :io
|
16
|
+
def initialize(io)
|
17
|
+
@io = io
|
18
|
+
end
|
19
|
+
|
20
|
+
include Enumerable
|
21
|
+
def each(&blk)
|
22
|
+
c_tnets_stream(io, &blk)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class TrueClass
|
28
|
+
TO_TNETS = '4:true!'
|
29
|
+
|
30
|
+
def to_tnets(*)
|
31
|
+
TO_TNETS
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class FalseClass
|
36
|
+
TO_TNETS = '5:false!'
|
37
|
+
def to_tnets(*)
|
38
|
+
TO_TNETS
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
class NilClass
|
43
|
+
TO_TNETS = '0:~'
|
44
|
+
def to_tnets(*)
|
45
|
+
TO_TNETS
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class String
|
50
|
+
def to_tnets(*)
|
51
|
+
"#{size}:#{self},"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
class Fixnum
|
56
|
+
def to_tnets(*)
|
57
|
+
"#{to_s.size}:#{self}#"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
module Enumerable
|
62
|
+
def to_tnets(*a, &b)
|
63
|
+
return each_tnets(&b) if block_given?
|
64
|
+
|
65
|
+
out = ''
|
66
|
+
each { |x| out << x.to_tnets(*a) }
|
67
|
+
"#{out.size}:#{out}]"
|
68
|
+
end
|
69
|
+
|
70
|
+
def stream_tnets(io=$stdout)
|
71
|
+
each_tnets { |x| io << x }
|
72
|
+
end
|
73
|
+
|
74
|
+
def each_tnets
|
75
|
+
each { |x| yield x.to_tnets }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
class Hash
|
80
|
+
def to_tnets(*)
|
81
|
+
out = ''
|
82
|
+
each do |k, v|
|
83
|
+
# symbols don't respond to :to_str for some reason
|
84
|
+
k = k.to_s if k.is_a? Symbol
|
85
|
+
|
86
|
+
unless k.respond_to? :to_str
|
87
|
+
raise TNETS::DumpError, "only string-like objects are allowed as keys"
|
88
|
+
end
|
89
|
+
|
90
|
+
out << k.to_str.to_tnets
|
91
|
+
out << v.to_tnets
|
92
|
+
end
|
93
|
+
|
94
|
+
"#{out.size}:#{out}}"
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
class Object
|
99
|
+
def to_tnets(*a, &b)
|
100
|
+
if respond_to? :as_tnets
|
101
|
+
as_tnets(*a).to_tnets
|
102
|
+
elsif respond_to? :as_json
|
103
|
+
as_json(*a).to_tnets
|
104
|
+
else
|
105
|
+
method_missing(:to_tnets, *a, &b)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
@@ -0,0 +1,182 @@
|
|
1
|
+
require './lib/tnetstrings'
|
2
|
+
|
3
|
+
describe TNETS do
|
4
|
+
context "parsing" do
|
5
|
+
context "successful examples" do
|
6
|
+
it "parses a string" do
|
7
|
+
TNETS.load("3:abc,").should == "abc"
|
8
|
+
end
|
9
|
+
|
10
|
+
it "parses null" do
|
11
|
+
TNETS.load("0:~").should == nil
|
12
|
+
end
|
13
|
+
|
14
|
+
it "parses true" do
|
15
|
+
TNETS.load("4:true!").should == true
|
16
|
+
end
|
17
|
+
|
18
|
+
it "parses false" do
|
19
|
+
TNETS.load("5:false!").should == false
|
20
|
+
end
|
21
|
+
|
22
|
+
it "parses a number" do
|
23
|
+
TNETS.load("2:32#").should == 32
|
24
|
+
end
|
25
|
+
|
26
|
+
it "parses a negative number" do
|
27
|
+
TNETS.load("3:-32#").should == -32
|
28
|
+
end
|
29
|
+
|
30
|
+
it "parses an array" do
|
31
|
+
TNETS.load("17:2:32#2:84#4:true!]").should == [32, 84, true]
|
32
|
+
end
|
33
|
+
|
34
|
+
it "parses a dict" do
|
35
|
+
TNETS.load(%[16:1:a,1:1#1:b,1:2#}]).should == {:a => 1, :b => 2}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
context "degenerate cases" do
|
40
|
+
it "parses an empty dict" do
|
41
|
+
TNETS.load(%|0:}|).should == {}
|
42
|
+
end
|
43
|
+
|
44
|
+
it "parses an empty array" do
|
45
|
+
TNETS.load(%|0:]|).should == []
|
46
|
+
end
|
47
|
+
|
48
|
+
it "parses an empty string" do
|
49
|
+
TNETS.load(%|0:,|).should == ''
|
50
|
+
end
|
51
|
+
|
52
|
+
it "parses an empty bool" do
|
53
|
+
TNETS.load(%|0:!|).should == false
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
context "error cases" do
|
58
|
+
def expect_error(str, *args)
|
59
|
+
expect { TNETS.load(str) }
|
60
|
+
.to raise_error(TNETS::ParserError, *args)
|
61
|
+
end
|
62
|
+
|
63
|
+
context "length spec" do
|
64
|
+
it "requires digits at the front" do
|
65
|
+
expect_error("abc:true!")
|
66
|
+
end
|
67
|
+
|
68
|
+
it "requires a colon before the 10th character" do
|
69
|
+
expect_error("12345678901:true!")
|
70
|
+
end
|
71
|
+
|
72
|
+
it "requires a valid symbol at the end" do
|
73
|
+
expect_error("1:f")
|
74
|
+
end
|
75
|
+
|
76
|
+
it "requires the data to be at least as long as the length spec" do
|
77
|
+
expect_error("100:true!")
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "null" do
|
82
|
+
it "requires the length to be zero" do
|
83
|
+
expect_error("1:x~")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
context "bool" do
|
88
|
+
it "returns false given anything but true" do
|
89
|
+
TNETS.load("4:trub!").should == false
|
90
|
+
TNETS.load("6:sdflkj!").should == false
|
91
|
+
TNETS.load("4:true!").should == true
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
context "number" do
|
96
|
+
it "requires digits only" do
|
97
|
+
expect_error("3:1bc#")
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
context "array" do
|
102
|
+
it "requires sane lengths" do
|
103
|
+
expect_error("5:10:abcde,]")
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
context "dict" do
|
108
|
+
it "requires sane lengths" do
|
109
|
+
expect_error("13:1:a,10:abcde,}")
|
110
|
+
end
|
111
|
+
|
112
|
+
it "requires keys to be strings" do
|
113
|
+
# { 1 => 2 }
|
114
|
+
expect_error("8:1:1#1:2#}")
|
115
|
+
# { nil => 2 }
|
116
|
+
expect_error("7:0:~1:2#}")
|
117
|
+
# { false => 2 }
|
118
|
+
expect_error("7:0:!1:2#}")
|
119
|
+
end
|
120
|
+
|
121
|
+
it "requires a balanced hash" do
|
122
|
+
# one item only
|
123
|
+
# { :a => }
|
124
|
+
expect_error("4:1:,a}")
|
125
|
+
|
126
|
+
# three items
|
127
|
+
# { :a => 1, :b => }
|
128
|
+
expect_error("12:1:a,1:1#1:b,}")
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
context "dumping" do
|
135
|
+
it "dumps a string" do
|
136
|
+
"abc".to_tnets.should == "3:abc,"
|
137
|
+
TNETS.load(TNETS.dump("abc")).should == "abc"
|
138
|
+
end
|
139
|
+
|
140
|
+
it "dumps a number" do
|
141
|
+
34.to_tnets.should == "2:34#"
|
142
|
+
TNETS.load(TNETS.dump(34)).should == 34
|
143
|
+
end
|
144
|
+
|
145
|
+
it "dumps nil" do
|
146
|
+
nil.to_tnets.should == '0:~'
|
147
|
+
TNETS.load(TNETS.dump(nil)).should == nil
|
148
|
+
end
|
149
|
+
|
150
|
+
it "dumps true and false" do
|
151
|
+
true.to_tnets.should == '4:true!'
|
152
|
+
false.to_tnets.should == '5:false!'
|
153
|
+
TNETS.load(TNETS.dump(true)).should == true
|
154
|
+
TNETS.load(TNETS.dump(false)).should == false
|
155
|
+
end
|
156
|
+
|
157
|
+
it "dumps an array" do
|
158
|
+
arr = [1, true, "three", nil]
|
159
|
+
arr.to_tnets.should == '22:1:1#4:true!5:three,0:~]'
|
160
|
+
TNETS.load(TNETS.dump(arr)).should == arr
|
161
|
+
end
|
162
|
+
|
163
|
+
it "streams an enumerable" do
|
164
|
+
sio = StringIO.new
|
165
|
+
enum = Class.new do
|
166
|
+
include Enumerable
|
167
|
+
def each(&b)
|
168
|
+
[1, true, "three", nil].each(&b)
|
169
|
+
end
|
170
|
+
end.new
|
171
|
+
|
172
|
+
enum.stream_tnets(sio)
|
173
|
+
sio.string.should == enum.map(&:to_tnets).join
|
174
|
+
end
|
175
|
+
|
176
|
+
it "dumps a hash" do
|
177
|
+
hsh = { :a => 1, :b => true, :c => "three", :d => nil }
|
178
|
+
hsh.to_tnets.should == '38:1:a,1:1#1:b,4:true!1:c,5:three,1:d,0:~}'
|
179
|
+
TNETS.load(TNETS.dump(hsh)).should == hsh
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
metadata
ADDED
@@ -0,0 +1,82 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tnetstrings
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jay Adkisson
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
|
13
|
+
date: 2011-07-27 00:00:00 -07:00
|
14
|
+
default_executable:
|
15
|
+
dependencies:
|
16
|
+
- !ruby/object:Gem::Dependency
|
17
|
+
name: rspec
|
18
|
+
prerelease: false
|
19
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
20
|
+
none: false
|
21
|
+
requirements:
|
22
|
+
- - ~>
|
23
|
+
- !ruby/object:Gem::Version
|
24
|
+
version: "2.0"
|
25
|
+
type: :development
|
26
|
+
version_requirements: *id001
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
prerelease: false
|
30
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
31
|
+
none: false
|
32
|
+
requirements:
|
33
|
+
- - ">="
|
34
|
+
- !ruby/object:Gem::Version
|
35
|
+
version: "0"
|
36
|
+
type: :development
|
37
|
+
version_requirements: *id002
|
38
|
+
description: A general-purpose tnetstrings library for Ruby
|
39
|
+
email: jjmadkisson@gmail.com
|
40
|
+
executables: []
|
41
|
+
|
42
|
+
extensions: []
|
43
|
+
|
44
|
+
extra_rdoc_files: []
|
45
|
+
|
46
|
+
files:
|
47
|
+
- ext/tnetstrings.c
|
48
|
+
- ext/tnetstrings.h
|
49
|
+
- ext/tnetstrings.so
|
50
|
+
- lib/tnetstrings.rb
|
51
|
+
- lib/tnetstrings/version.rb
|
52
|
+
- spec/tnetstrings_spec.rb
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://github.com/causes/iplogic
|
55
|
+
licenses: []
|
56
|
+
|
57
|
+
post_install_message:
|
58
|
+
rdoc_options: []
|
59
|
+
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
none: false
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: "0"
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
none: false
|
70
|
+
requirements:
|
71
|
+
- - ">="
|
72
|
+
- !ruby/object:Gem::Version
|
73
|
+
version: "0"
|
74
|
+
requirements: []
|
75
|
+
|
76
|
+
rubyforge_project:
|
77
|
+
rubygems_version: 1.5.2
|
78
|
+
signing_key:
|
79
|
+
specification_version: 3
|
80
|
+
summary: A general-purpose tnetstrings library for Ruby
|
81
|
+
test_files:
|
82
|
+
- spec/tnetstrings_spec.rb
|