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