contraction 0.2.6 → 0.3.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/lib/parser.rb +1 -0
- data/lib/parser/type.rb +7 -121
- data/lib/parser/type_parser.rb +374 -0
- metadata +8 -12
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: e0f7aa54dcbe857b462c9f55a7b72e5462b25cec
|
|
4
|
+
data.tar.gz: 5b338f1f833a9434b61dfd0b423387398a6789ce
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 4e31cc128163153debc169166fca37bb472473bfb55f24da2a629dbbe577e3b2080b001820c905cec99825b9942f74e2148c6d1905a5a289291bd724d175eebf
|
|
7
|
+
data.tar.gz: f73b4bb2677a13d62c56d935647d1775ab3480e7b5f68f1f18343f0f02010db443d20614a3e4e9b95f3691ef0ff92835d13420e7ad8b984bed163f13c5115cce
|
data/lib/parser.rb
CHANGED
data/lib/parser/type.rb
CHANGED
|
@@ -1,138 +1,24 @@
|
|
|
1
|
-
# FIXME: Actually use the type parser in the actual parser... Duh.
|
|
2
1
|
module Contraction
|
|
3
2
|
module Parser
|
|
4
3
|
class Type
|
|
5
|
-
attr_reader :
|
|
6
|
-
|
|
4
|
+
attr_reader :type
|
|
7
5
|
def initialize(part)
|
|
8
|
-
@legal_types = []
|
|
9
|
-
@method_requirements = []
|
|
10
|
-
@length = -1
|
|
11
|
-
@key_types = []
|
|
12
|
-
@value_types = []
|
|
13
|
-
|
|
14
6
|
parse(part)
|
|
15
7
|
end
|
|
16
8
|
|
|
17
9
|
# Checks weather or not thing is a given type.
|
|
18
|
-
# @param [String] thing A string containing a type definition. For example:
|
|
19
|
-
# Array<String>
|
|
20
10
|
def check(thing)
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
check_length(thing) &&
|
|
24
|
-
check_hash(thing)
|
|
11
|
+
return true unless type
|
|
12
|
+
type.check thing
|
|
25
13
|
end
|
|
26
14
|
|
|
27
15
|
private
|
|
28
16
|
|
|
29
17
|
def parse(line)
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
parse_short_hash_or_reference(line) ||
|
|
35
|
-
parse_regular(line)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def parse_typed_container(line)
|
|
39
|
-
return unless line.include? '<'
|
|
40
|
-
# It's some kind of container that can only hold certain things
|
|
41
|
-
list = line.match(/\<(?<list>[^\>]+)\>/)['list']
|
|
42
|
-
list.split(',').each do |type|
|
|
43
|
-
@legal_types << Type.new(type.strip)
|
|
44
|
-
end
|
|
45
|
-
true
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def parse_duck_type(line)
|
|
49
|
-
return unless line =~ /^#/
|
|
50
|
-
# It's a duck-typed object of some kind
|
|
51
|
-
methods = line.split(",").map { |p| p.strip.gsub(/^#/,'').to_sym }
|
|
52
|
-
@method_requirements += methods
|
|
53
|
-
true
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
def parse_fixed_list(line)
|
|
57
|
-
return unless line.include?('(')
|
|
58
|
-
# It's a fixed-length list
|
|
59
|
-
list = line.match(/\((?<list>[^\>]+)\)/)['list']
|
|
60
|
-
parts = list.split(',')
|
|
61
|
-
@length = parts.length
|
|
62
|
-
parts.each do |type|
|
|
63
|
-
@legal_types << Type.new(type.strip)
|
|
64
|
-
end
|
|
65
|
-
true
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
def parse_hash(line)
|
|
69
|
-
return unless line.include? 'Hash{'
|
|
70
|
-
# It's a hash with specific key-value pair types
|
|
71
|
-
parts = line.match(/\{(?<key_types>.+)\s*=\>\s*(?<value_types>[^\}]+)\}/)
|
|
72
|
-
@key_types = parts['key_types'].split(',').map { |t| t.include?('#') ? t.strip.gsub(/^#/, '').to_sym : t.strip.constantize }
|
|
73
|
-
@value_types = parts['value_types'].split(',').map { |t| t.include?('#') ? t.strip.gsub(/^#/, '').to_sym : t.strip.constantize }
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
def parse_short_hash_or_reference(line)
|
|
77
|
-
return unless line.include? '{'
|
|
78
|
-
if parts = line.match(/\{(?<key_types>.+)\s*=\>\s*(?<value_types>[^\}]+)\}/)
|
|
79
|
-
@key_types = parts['key_types'].split(',').map { |t| t.include?('#') ? t.strip.gsub(/^#/, '').to_sym : t.strip.constantize }
|
|
80
|
-
@value_types = parts['value_types'].split(',').map { |t| t.include?('#') ? t.strip.gsub(/^#/, '').to_sym : t.strip.constantize }
|
|
81
|
-
else
|
|
82
|
-
# It's a reference to another documented type defined someplace in
|
|
83
|
-
# the codebase. We can ignore the reference, and treat it like a
|
|
84
|
-
# normal type.
|
|
85
|
-
@legal_types << line.gsub(/\{|\}/, '').constantize
|
|
86
|
-
end
|
|
87
|
-
true
|
|
88
|
-
end
|
|
89
|
-
|
|
90
|
-
def parse_regular(line)
|
|
91
|
-
# It's a regular-ass type.
|
|
92
|
-
@legal_types << line.constantize
|
|
93
|
-
end
|
|
94
|
-
|
|
95
|
-
def check_hash(thing)
|
|
96
|
-
return true if @key_types.empty? or @value_types.empty?
|
|
97
|
-
return false unless thing.is_a?(Hash)
|
|
98
|
-
thing.keys.all? do |k|
|
|
99
|
-
@key_types.any? { |kt| kt.is_a?(Symbol) ? k.respond_to?(kt) : k.is_a?(kt) }
|
|
100
|
-
end &&
|
|
101
|
-
thing.values.all? do |v|
|
|
102
|
-
@value_types.any? { |vt| vt.is_a?(Symbol) ? v.respond_to?(vt) : v.is_a?(vt) }
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
|
|
106
|
-
def check_length(thing)
|
|
107
|
-
return true if @length == -1
|
|
108
|
-
thing.length == @length
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
def check_duck_typing(thing)
|
|
112
|
-
return true if @method_requirements.empty?
|
|
113
|
-
@method_requirements.all? do |m|
|
|
114
|
-
thing.respond_to? m
|
|
115
|
-
end
|
|
116
|
-
end
|
|
117
|
-
|
|
118
|
-
def check_types(thing)
|
|
119
|
-
return true if @legal_types.empty?
|
|
120
|
-
if thing.is_a? Enumerable
|
|
121
|
-
types = @legal_types.map { |t| t.respond_to?(:legal_types) ? t.legal_types : t }.flatten
|
|
122
|
-
return thing.all? { |th| types.include?(th.class) }
|
|
123
|
-
else
|
|
124
|
-
@legal_types.any? do |t|
|
|
125
|
-
if t.is_a?(Contraction::Parser::Type)
|
|
126
|
-
# Given the fact that we check enumerables above, we should never be here.
|
|
127
|
-
next false
|
|
128
|
-
end
|
|
129
|
-
if thing.is_a?(Enumerable)
|
|
130
|
-
thing.all? { |th| th.is_a?(t) }
|
|
131
|
-
else
|
|
132
|
-
thing.is_a?(t)
|
|
133
|
-
end
|
|
134
|
-
end
|
|
135
|
-
end
|
|
18
|
+
@type = Contraction::TypeParser.parse(line).first
|
|
19
|
+
rescue => e
|
|
20
|
+
puts e
|
|
21
|
+
@type = nil
|
|
136
22
|
end
|
|
137
23
|
end
|
|
138
24
|
end
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
module Contraction
|
|
2
|
+
# The lexer scans the input, creating a stack of tokens that can be used
|
|
3
|
+
# to then figure out our parse tree.
|
|
4
|
+
class TypeLexer
|
|
5
|
+
TOKENS = [
|
|
6
|
+
/^Hash/,
|
|
7
|
+
/^=>/,
|
|
8
|
+
/^\{/, /^\}/,
|
|
9
|
+
/^\[/, /^\]/,
|
|
10
|
+
/^\(/, /^\)/,
|
|
11
|
+
/^</, /^>/,
|
|
12
|
+
/^,/,
|
|
13
|
+
/^#/,
|
|
14
|
+
/([a-z_]+[a-z0-9_]*|(H(?!ash)))?[^=\{\[\(<>\)\]\},#]+/
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
def self.lex(text)
|
|
18
|
+
stack = []
|
|
19
|
+
while text.length > 0
|
|
20
|
+
changed = false
|
|
21
|
+
|
|
22
|
+
TOKENS.each do |r|
|
|
23
|
+
if m = text.match(r)
|
|
24
|
+
if m[0].strip != ''
|
|
25
|
+
stack << m[0].strip
|
|
26
|
+
end
|
|
27
|
+
text.sub! r, ''
|
|
28
|
+
changed = true
|
|
29
|
+
break
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
raise "Unknown token found at #{text}" unless changed
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
stack.reverse
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
class Type
|
|
41
|
+
attr_reader :klass
|
|
42
|
+
def initialize(klass)
|
|
43
|
+
@klass = klass
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def works_as_a?(thing)
|
|
47
|
+
thing == klass || thing.is_a?(klass)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def check(thing)
|
|
51
|
+
works_as_a?(thing)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class DuckType
|
|
56
|
+
attr_reader :method
|
|
57
|
+
def initialize(method)
|
|
58
|
+
@method = method
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def check(thing)
|
|
62
|
+
thing.respond_to? method.to_sym
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
class TypeList
|
|
67
|
+
attr_reader :types
|
|
68
|
+
def initialize(things)
|
|
69
|
+
@types = things.flatten
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def works_as_a?(thing)
|
|
73
|
+
types.any? { |t| t.works_as_a? thing }
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def check(thing)
|
|
77
|
+
# The only time that we need to match all instead of any is with
|
|
78
|
+
# duck-typing, so we just special-case it here.
|
|
79
|
+
if types.all? { |t| t.is_a? Contraction::DuckType }
|
|
80
|
+
return types.all? { |t| t.check(thing) }
|
|
81
|
+
else
|
|
82
|
+
return types.any? { |t| t.check(thing) }
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def size
|
|
87
|
+
types.size
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
class HashType
|
|
92
|
+
attr_reader :key_type, :value_type
|
|
93
|
+
def initialize(key_type, value_type)
|
|
94
|
+
@key_type = key_type
|
|
95
|
+
@value_type = value_type
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def check(thing)
|
|
99
|
+
thing.is_a?(Hash) &&
|
|
100
|
+
thing.keys.all? { |k| key_type.check(k) } &&
|
|
101
|
+
thing.values.all? { |v| value_type.check(v) }
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
class TypedContainer
|
|
106
|
+
attr_reader :type_list, :class_name
|
|
107
|
+
|
|
108
|
+
def initialize(class_type, type_list)
|
|
109
|
+
@type_list = type_list
|
|
110
|
+
@class_name = class_type
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def works_as_a?(thing)
|
|
114
|
+
type_list.works_as_a? thing
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def check(thing)
|
|
118
|
+
return false if !class_name.nil? && !class_name.check(thing)
|
|
119
|
+
thing.all? { |v| type_list.works_as_a? v }
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
class SizedContainer < TypedContainer
|
|
124
|
+
def check(thing)
|
|
125
|
+
super && thing.size == type_list.size
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
class ReferenceType
|
|
130
|
+
attr_reader :klass
|
|
131
|
+
def initialize(klass)
|
|
132
|
+
@klass = klass
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def check(thing)
|
|
136
|
+
thing.is_a? klass
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
class TypeParser
|
|
141
|
+
def self.parse(string)
|
|
142
|
+
@stack = TypeLexer.lex(string)
|
|
143
|
+
|
|
144
|
+
# We are going to walk though this one at a time, popping off the
|
|
145
|
+
# end, and seeing if the list of thing we have so far matches any
|
|
146
|
+
# known rules, being as greedy as possible.
|
|
147
|
+
things = [:typed_container, :sized_container, :type_list, :reference, :hash, :duck_type]
|
|
148
|
+
something_happened = false
|
|
149
|
+
data = []
|
|
150
|
+
begin
|
|
151
|
+
something_happened = false
|
|
152
|
+
things.each do |t|
|
|
153
|
+
thing = send(t)
|
|
154
|
+
if thing
|
|
155
|
+
data << thing
|
|
156
|
+
something_happened = true
|
|
157
|
+
end
|
|
158
|
+
end
|
|
159
|
+
end while something_happened
|
|
160
|
+
|
|
161
|
+
raise "Type parse error #{@stack.reverse.join ' '}" unless @stack.compact.empty?
|
|
162
|
+
data.flatten
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# A class name is anything that has a capitol first-letter
|
|
166
|
+
def self.class_name
|
|
167
|
+
thing = @stack.pop
|
|
168
|
+
return nil if thing.nil?
|
|
169
|
+
if thing[0] =~ /^[A-Z]/
|
|
170
|
+
return Type.new(thing.constantize)
|
|
171
|
+
else
|
|
172
|
+
@stack.push thing
|
|
173
|
+
return nil
|
|
174
|
+
end
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
# A duck-type is a thing prefaced with '#', indicating that it must have
|
|
178
|
+
# that method.
|
|
179
|
+
def self.duck_type
|
|
180
|
+
thing = @stack.pop
|
|
181
|
+
return nil if thing.nil?
|
|
182
|
+
if thing != '#'
|
|
183
|
+
@stack.push thing
|
|
184
|
+
return nil
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
DuckType.new @stack.pop
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# A type is either hash, or any class-name like thing
|
|
191
|
+
def self.type
|
|
192
|
+
reference || hash || typed_container || sized_container || class_name || duck_type
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# A type-list is a Type, optionally followed by a comma and another
|
|
196
|
+
# type-list
|
|
197
|
+
def self.type_list
|
|
198
|
+
things = []
|
|
199
|
+
things << type
|
|
200
|
+
return nil if things.first.nil?
|
|
201
|
+
|
|
202
|
+
things << @stack.pop
|
|
203
|
+
if things.last != ','
|
|
204
|
+
@stack.push things.pop
|
|
205
|
+
return TypeList.new things
|
|
206
|
+
end
|
|
207
|
+
things.pop # Remove the ',' from the list
|
|
208
|
+
|
|
209
|
+
things << type_list.types
|
|
210
|
+
TypeList.new(things.flatten)
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# A hash starts with an optional "Hash", and this then followed by an
|
|
214
|
+
# opening {, followed by a type-list, followed by a fat arrow ("=>"),
|
|
215
|
+
# followed by another type-list, followed by a closing curly brace
|
|
216
|
+
# ("}")
|
|
217
|
+
def self.hash
|
|
218
|
+
things = []
|
|
219
|
+
things << @stack.pop
|
|
220
|
+
if things.first != 'Hash' && things.first != '{'
|
|
221
|
+
things.size.times { @stack.push things.pop }
|
|
222
|
+
return nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
if things.first == 'Hash'
|
|
226
|
+
things << @stack.pop
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
if things.last != '{'
|
|
230
|
+
things.size.times { @stack.push things.pop }
|
|
231
|
+
return nil
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
# Get the first type
|
|
235
|
+
key_type = type_list
|
|
236
|
+
if !key_type
|
|
237
|
+
things.size.times { @stack.push things.pop }
|
|
238
|
+
return nil
|
|
239
|
+
else
|
|
240
|
+
things << key_type
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
# And the arrow
|
|
244
|
+
things << @stack.pop
|
|
245
|
+
if things.last != '=>'
|
|
246
|
+
things.size.times { @stack.push things.pop }
|
|
247
|
+
return nil
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
# And the value type
|
|
251
|
+
value_type = type_list
|
|
252
|
+
if !value_type
|
|
253
|
+
things.size.times { @stack.push things.pop }
|
|
254
|
+
return nil
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
# Finally, the colosing brace
|
|
258
|
+
things << @stack.pop
|
|
259
|
+
if things.last != '}'
|
|
260
|
+
things.size.times { @stack.push things.pop }
|
|
261
|
+
return nil
|
|
262
|
+
end
|
|
263
|
+
|
|
264
|
+
HashType.new(key_type, value_type)
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# A typed container is an optional class type, followed by a '<', followed
|
|
268
|
+
# by a type list, followed by a '>'
|
|
269
|
+
def self.typed_container
|
|
270
|
+
class_type = class_name
|
|
271
|
+
|
|
272
|
+
bracket = @stack.pop
|
|
273
|
+
if bracket.nil?
|
|
274
|
+
if class_type
|
|
275
|
+
@stack.push class_type.klass.to_s
|
|
276
|
+
end
|
|
277
|
+
return nil
|
|
278
|
+
end
|
|
279
|
+
if bracket != '<'
|
|
280
|
+
@stack.push bracket
|
|
281
|
+
|
|
282
|
+
if class_type
|
|
283
|
+
@stack.push class_type.klass.to_s
|
|
284
|
+
end
|
|
285
|
+
return nil
|
|
286
|
+
end
|
|
287
|
+
|
|
288
|
+
types = type_list
|
|
289
|
+
if !types
|
|
290
|
+
@stack.push bracket
|
|
291
|
+
|
|
292
|
+
if class_type
|
|
293
|
+
@stack.push class_type.klass.to_s
|
|
294
|
+
end
|
|
295
|
+
return nil
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
bracket2 = @stack.pop
|
|
299
|
+
if bracket2.nil? || bracket2 != '>'
|
|
300
|
+
raise "Expected '>', got #{bracket2}: #{@stack.inspect}"
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
TypedContainer.new(class_type, types)
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# A reference is a "{" followed by a type, followed by a "}"
|
|
307
|
+
def self.reference
|
|
308
|
+
b = @stack.pop
|
|
309
|
+
return nil if b.nil?
|
|
310
|
+
if b != '{'
|
|
311
|
+
@stack.push b
|
|
312
|
+
return nil
|
|
313
|
+
end
|
|
314
|
+
|
|
315
|
+
t = class_name
|
|
316
|
+
if !t
|
|
317
|
+
@stack.push b
|
|
318
|
+
return nil
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
b2 = @stack.pop
|
|
322
|
+
if b2.nil? || b2 != '}'
|
|
323
|
+
@stack.push b2 if b2
|
|
324
|
+
@stack.push t.klass.to_s
|
|
325
|
+
@stack.push b
|
|
326
|
+
return nil
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
return ReferenceType.new t
|
|
330
|
+
end
|
|
331
|
+
|
|
332
|
+
# A sized container is like a typed container, except it has a type for
|
|
333
|
+
# every member of the set. So if there are 3 types in the type list, the
|
|
334
|
+
# final type must be a container with exactly three members, conforming to
|
|
335
|
+
# their respective types. An example would be a Vector3 class, with the
|
|
336
|
+
# initializer defined as either 3 floats, or a Array[Float, Float, Float]
|
|
337
|
+
def self.sized_container
|
|
338
|
+
class_type = class_name
|
|
339
|
+
|
|
340
|
+
bracket = @stack.pop
|
|
341
|
+
if bracket.nil?
|
|
342
|
+
if class_type
|
|
343
|
+
@stack.push class_type.klass.to_s
|
|
344
|
+
end
|
|
345
|
+
return nil
|
|
346
|
+
end
|
|
347
|
+
if bracket != '('
|
|
348
|
+
@stack.push bracket
|
|
349
|
+
|
|
350
|
+
if class_type
|
|
351
|
+
@stack.push class_type.klass.to_s
|
|
352
|
+
end
|
|
353
|
+
return nil
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
types = type_list
|
|
357
|
+
if !types
|
|
358
|
+
@stack.push bracket
|
|
359
|
+
|
|
360
|
+
if class_type
|
|
361
|
+
@stack.push class_type.klass.to_s
|
|
362
|
+
end
|
|
363
|
+
return nil
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
bracket2 = @stack.pop
|
|
367
|
+
if bracket2.nil? || bracket2 != ')'
|
|
368
|
+
raise "Expected ']', got #{bracket2}: #{@stack.inspect}"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
SizedContainer.new(class_type, types)
|
|
372
|
+
end
|
|
373
|
+
end
|
|
374
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: contraction
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
5
|
-
prerelease:
|
|
4
|
+
version: 0.3.0
|
|
6
5
|
platform: ruby
|
|
7
6
|
authors:
|
|
8
7
|
- Thomas Luce
|
|
9
8
|
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date: 2014-07-
|
|
11
|
+
date: 2014-07-22 00:00:00.000000000 Z
|
|
13
12
|
dependencies: []
|
|
14
13
|
description: Using RDoc documentation as your contract definition, you get solid code,
|
|
15
14
|
and good docs. Win-win!
|
|
@@ -26,32 +25,29 @@ files:
|
|
|
26
25
|
- lib/parser.rb
|
|
27
26
|
- lib/parser/lines.rb
|
|
28
27
|
- lib/parser/type.rb
|
|
28
|
+
- lib/parser/type_parser.rb
|
|
29
29
|
- lib/string.rb
|
|
30
30
|
homepage: https://github.com/thomasluce/contraction
|
|
31
31
|
licenses: []
|
|
32
|
+
metadata: {}
|
|
32
33
|
post_install_message:
|
|
33
34
|
rdoc_options: []
|
|
34
35
|
require_paths:
|
|
35
36
|
- lib
|
|
36
37
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
37
|
-
none: false
|
|
38
38
|
requirements:
|
|
39
|
-
- -
|
|
39
|
+
- - '>='
|
|
40
40
|
- !ruby/object:Gem::Version
|
|
41
41
|
version: '0'
|
|
42
|
-
segments:
|
|
43
|
-
- 0
|
|
44
|
-
hash: -2650941166078552798
|
|
45
42
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
46
|
-
none: false
|
|
47
43
|
requirements:
|
|
48
|
-
- -
|
|
44
|
+
- - '>='
|
|
49
45
|
- !ruby/object:Gem::Version
|
|
50
46
|
version: '0'
|
|
51
47
|
requirements: []
|
|
52
48
|
rubyforge_project:
|
|
53
|
-
rubygems_version:
|
|
49
|
+
rubygems_version: 2.2.2
|
|
54
50
|
signing_key:
|
|
55
|
-
specification_version:
|
|
51
|
+
specification_version: 4
|
|
56
52
|
summary: A simple desgin-by-contract library
|
|
57
53
|
test_files: []
|