explain 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/.gitignore +17 -0
- data/.rvmrc +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +69 -0
- data/Rakefile +10 -0
- data/bin/explain +5 -0
- data/examples/person.rb +10 -0
- data/explain.gemspec +19 -0
- data/lib/explain.rb +10 -0
- data/lib/explain/version.rb +3 -0
- data/lib/explain/visitor.rb +337 -0
- data/test/explain/visitor_test.rb +90 -0
- data/test/test_helper.rb +2 -0
- metadata +62 -0
data/.gitignore
ADDED
data/.rvmrc
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
rvm use rbx-head@explain --create
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Josep M. Bach
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,69 @@
|
|
1
|
+
# explain
|
2
|
+
|
3
|
+
Explain explains your Ruby code in natural language. It is intended to be a
|
4
|
+
tool for beginners who aren't yet very familiar with programming.
|
5
|
+
|
6
|
+
It is a work in progress (a bit rough on the edges), so don't be mad. It will
|
7
|
+
get better over time ;)
|
8
|
+
|
9
|
+
(Explain runs only on Rubinius.)
|
10
|
+
|
11
|
+
## Installation
|
12
|
+
|
13
|
+
Install Rubinius if you don't have it yet:
|
14
|
+
|
15
|
+
$ rvm install rbx-head
|
16
|
+
$ rvm use rbx-head
|
17
|
+
|
18
|
+
Install explain as a gem:
|
19
|
+
|
20
|
+
$ gem install explain
|
21
|
+
|
22
|
+
## Usage
|
23
|
+
|
24
|
+
Given some example Ruby code, for example this in `examples/person.rb`:
|
25
|
+
|
26
|
+
```ruby
|
27
|
+
class Person
|
28
|
+
def walk(distance)
|
29
|
+
@distance += distance
|
30
|
+
@hunger += 2
|
31
|
+
end
|
32
|
+
|
33
|
+
def eat(food)
|
34
|
+
@hunger -= food.nutritional_value
|
35
|
+
end
|
36
|
+
end
|
37
|
+
```
|
38
|
+
|
39
|
+
We execute `explain` from the command line:
|
40
|
+
|
41
|
+
$ explain examples/person.rb
|
42
|
+
|
43
|
+
And it will output:
|
44
|
+
|
45
|
+
Let's describe the general attributes and behavior of any Person.
|
46
|
+
|
47
|
+
A Person can **walk**, given a specific distance. This is described as
|
48
|
+
follows: its distance will be its distance plus what we previously defined as
|
49
|
+
`distance`. Finally we return its hunger will be its hunger plus the number
|
50
|
+
2..
|
51
|
+
|
52
|
+
A Person can **eat**, given a specific food. This is described as follows:
|
53
|
+
Finally we return its hunger will be its hunger minus the result of calling
|
54
|
+
**nutritional_value** on what we previously defined as `food`..
|
55
|
+
And with this we're done describing a Person.
|
56
|
+
|
57
|
+
## Contributing
|
58
|
+
|
59
|
+
1. Fork it
|
60
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
61
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
62
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
63
|
+
5. Create new Pull Request
|
64
|
+
|
65
|
+
## Who's this
|
66
|
+
|
67
|
+
This was made by [Josep M. Bach (Txus)](http://txustice.me) under the MIT
|
68
|
+
license. I'm [@txustice](http://twitter.com/txustice) on twitter (where you
|
69
|
+
should probably follow me!).
|
data/Rakefile
ADDED
data/bin/explain
ADDED
data/examples/person.rb
ADDED
data/explain.gemspec
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'explain/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "explain"
|
8
|
+
gem.version = Explain::VERSION
|
9
|
+
gem.authors = ["Josep M. Bach"]
|
10
|
+
gem.email = ["josep.m.bach@gmail.com"]
|
11
|
+
gem.description = %q{Explain explains your Ruby code in natural language.}
|
12
|
+
gem.summary = %q{Explain explains your Ruby code in natural language.}
|
13
|
+
gem.homepage = "https://github.com/txus/explain"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
end
|
data/lib/explain.rb
ADDED
@@ -0,0 +1,337 @@
|
|
1
|
+
module Explain
|
2
|
+
class Visitor
|
3
|
+
def initialize
|
4
|
+
@output = []
|
5
|
+
end
|
6
|
+
|
7
|
+
def visit(node, in_sentence=false)
|
8
|
+
__send__ node.node_name, node, in_sentence
|
9
|
+
end
|
10
|
+
|
11
|
+
def emit(str)
|
12
|
+
@output.push str
|
13
|
+
end
|
14
|
+
|
15
|
+
def output
|
16
|
+
@output.join
|
17
|
+
end
|
18
|
+
|
19
|
+
def class(node, in_sentence=false)
|
20
|
+
@in_class = node.name.name
|
21
|
+
emit "Let's describe the general attributes and behavior of any #{node.name.name}."
|
22
|
+
visit node.body
|
23
|
+
emit "\nAnd with this we're done describing a #{node.name.name}."
|
24
|
+
@in_class = nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def module(node, in_sentence=false)
|
28
|
+
@in_class = node.name.name
|
29
|
+
emit "Let's describe a collection of behavior that we'll call #{node.name.name}."
|
30
|
+
visit node.body
|
31
|
+
emit "\nAnd with this we're done describing #{node.name.name}."
|
32
|
+
@in_class = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def empty_body(*)
|
36
|
+
# do nothing
|
37
|
+
end
|
38
|
+
|
39
|
+
def class_scope(node, in_sentence=false)
|
40
|
+
visit node.body
|
41
|
+
end
|
42
|
+
alias module_scope class_scope
|
43
|
+
|
44
|
+
def local_variable_assignment(node, in_sentence=false)
|
45
|
+
emit in_sentence ? "w" : "W"
|
46
|
+
emit "e define as `#{node.name}` "
|
47
|
+
visit(node.value)
|
48
|
+
end
|
49
|
+
|
50
|
+
def local_variable_access(node, in_sentence=false)
|
51
|
+
emit "what we previously defined as `#{node.name}`"
|
52
|
+
end
|
53
|
+
|
54
|
+
def instance_variable_assignment(node, in_sentence=false)
|
55
|
+
emit in_sentence ? "i" : "I"
|
56
|
+
emit "ts #{node.name[1..-1]} will be "
|
57
|
+
visit(node.value)
|
58
|
+
end
|
59
|
+
|
60
|
+
def instance_variable_access(node, in_sentence=false)
|
61
|
+
emit "its #{node.name[1..-1]}"
|
62
|
+
end
|
63
|
+
|
64
|
+
def fixnum_literal(node, in_sentence=false)
|
65
|
+
emit "the number #{node.value}"
|
66
|
+
end
|
67
|
+
|
68
|
+
def float_literal(node, in_sentence=false)
|
69
|
+
emit "the number #{node.value}"
|
70
|
+
end
|
71
|
+
|
72
|
+
def string_literal(node, in_sentence=false)
|
73
|
+
emit "the word \"#{node.string}\""
|
74
|
+
end
|
75
|
+
|
76
|
+
def symbol_literal(node, in_sentence=false)
|
77
|
+
emit "the name `#{node.value}`"
|
78
|
+
end
|
79
|
+
|
80
|
+
def true_literal(node, in_sentence=false)
|
81
|
+
emit "the truth"
|
82
|
+
end
|
83
|
+
|
84
|
+
def false_literal(node, in_sentence=false)
|
85
|
+
emit "the falsehood"
|
86
|
+
end
|
87
|
+
|
88
|
+
def nil_literal(node, in_sentence=false)
|
89
|
+
emit "the nothingness"
|
90
|
+
end
|
91
|
+
|
92
|
+
def empty_array(node, in_sentence=false)
|
93
|
+
emit "an empty list"
|
94
|
+
end
|
95
|
+
|
96
|
+
def array_literal(node, in_sentence=false)
|
97
|
+
body = node.body
|
98
|
+
emit "a list of "
|
99
|
+
body.each_with_index do |node, index|
|
100
|
+
visit node
|
101
|
+
if body.length == index + 2 # last element
|
102
|
+
emit " and "
|
103
|
+
elsif body.length != index + 1
|
104
|
+
emit ", "
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def hash_literal(node, in_sentence=false)
|
110
|
+
return emit "an empty dictionary" if node.array.empty?
|
111
|
+
|
112
|
+
body = node.array.each_slice(2)
|
113
|
+
|
114
|
+
emit 'a dictionary where '
|
115
|
+
body.each_with_index do |slice, index|
|
116
|
+
key, value = slice
|
117
|
+
|
118
|
+
visit key
|
119
|
+
emit " relates to "
|
120
|
+
visit value
|
121
|
+
|
122
|
+
if body.to_a.length == index + 2 # last element
|
123
|
+
emit " and "
|
124
|
+
elsif body.to_a.length != index + 1
|
125
|
+
emit ", "
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
# def range(node, in_sentence=false)
|
131
|
+
# end
|
132
|
+
|
133
|
+
# def range_exclude(node, in_sentence=false)
|
134
|
+
# end
|
135
|
+
|
136
|
+
# def regex_literal(node, in_sentence=false)
|
137
|
+
# end
|
138
|
+
|
139
|
+
def send(node, in_sentence=false)
|
140
|
+
if node.receiver.is_a?(Rubinius::AST::Self)
|
141
|
+
emit in_sentence ? "the result of calling " : "We "
|
142
|
+
emit "**#{node.name}**"
|
143
|
+
else
|
144
|
+
emit in_sentence ? "the result of calling " : "We call "
|
145
|
+
emit "**#{node.name}** on "
|
146
|
+
visit node.receiver
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def send_with_arguments(node, in_sentence=false)
|
151
|
+
return if process_binary_operator(node, in_sentence) # 1 * 2, a / 3, true && false
|
152
|
+
|
153
|
+
if node.receiver.is_a?(Rubinius::AST::Self)
|
154
|
+
emit in_sentence ? "the result of calling " : "We "
|
155
|
+
emit "**#{node.name}**"
|
156
|
+
else
|
157
|
+
emit in_sentence ? "the result of calling " : "We call "
|
158
|
+
emit "**#{node.name}** on "
|
159
|
+
visit node.receiver
|
160
|
+
end
|
161
|
+
|
162
|
+
unless node.arguments.array.empty?
|
163
|
+
emit " given "
|
164
|
+
visit(node.arguments)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def actual_arguments(node, in_sentence=false)
|
169
|
+
body = node.array
|
170
|
+
body.each_with_index do |node, index|
|
171
|
+
visit node
|
172
|
+
if body.length == index + 2 # last element
|
173
|
+
emit " and "
|
174
|
+
elsif body.length != index + 1
|
175
|
+
emit ", "
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
|
180
|
+
# def iter_arguments(node, in_sentence=false)
|
181
|
+
# end
|
182
|
+
|
183
|
+
# def iter(node, in_sentence=false)
|
184
|
+
# end
|
185
|
+
|
186
|
+
def block(node, in_sentence=false)
|
187
|
+
body = node.array
|
188
|
+
body.each_with_index do |node, index|
|
189
|
+
if body.length == index + 1 # last element
|
190
|
+
if @in_method
|
191
|
+
emit "Finally we return "
|
192
|
+
end
|
193
|
+
visit node, true
|
194
|
+
emit "."
|
195
|
+
else
|
196
|
+
visit node, in_sentence
|
197
|
+
emit '. '
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# def not(node, in_sentence=false)
|
203
|
+
# end
|
204
|
+
|
205
|
+
# def and(node, in_sentence=false)
|
206
|
+
# end
|
207
|
+
|
208
|
+
# def or(node, in_sentence=false)
|
209
|
+
# end
|
210
|
+
|
211
|
+
# def op_assign_and(node, in_sentence=false)
|
212
|
+
# end
|
213
|
+
|
214
|
+
# def op_assign_or(node, in_sentence=false)
|
215
|
+
# end
|
216
|
+
|
217
|
+
# def toplevel_constant(node, in_sentence=false)
|
218
|
+
# end
|
219
|
+
|
220
|
+
# def constant_access(node, in_sentence=false)
|
221
|
+
# end
|
222
|
+
|
223
|
+
# def scoped_constant(node, in_sentence=false)
|
224
|
+
# end
|
225
|
+
|
226
|
+
def if(node, in_sentence=false)
|
227
|
+
body, else_body = node.body, node.else
|
228
|
+
keyword = 'if'
|
229
|
+
|
230
|
+
if node.body.is_a?(Rubinius::AST::NilLiteral) && !node.else.is_a?(Rubinius::AST::NilLiteral)
|
231
|
+
|
232
|
+
body, else_body = else_body, body
|
233
|
+
keyword = 'unless'
|
234
|
+
end
|
235
|
+
|
236
|
+
emit "#{keyword} "
|
237
|
+
visit node.condition, true
|
238
|
+
emit " is truthy, then "
|
239
|
+
|
240
|
+
visit body, true
|
241
|
+
|
242
|
+
if else_body.is_a?(Rubinius::AST::NilLiteral)
|
243
|
+
return
|
244
|
+
end
|
245
|
+
|
246
|
+
emit " -- else, "
|
247
|
+
|
248
|
+
visit else_body, true
|
249
|
+
end
|
250
|
+
|
251
|
+
def while(node, in_sentence=false)
|
252
|
+
emit in_sentence ? "w" : "W"
|
253
|
+
emit 'hile '
|
254
|
+
visit node.condition, true
|
255
|
+
emit ", "
|
256
|
+
visit node.body, true
|
257
|
+
end
|
258
|
+
|
259
|
+
def until(node, in_sentence=false)
|
260
|
+
emit in_sentence ? "u" : "U"
|
261
|
+
emit 'ntil '
|
262
|
+
visit node.condition, true
|
263
|
+
emit ", "
|
264
|
+
visit node.body, true
|
265
|
+
end
|
266
|
+
|
267
|
+
def return(node, in_sentence=false)
|
268
|
+
emit in_sentence ? "w" : "W"
|
269
|
+
emit "e return "
|
270
|
+
visit node.value
|
271
|
+
end
|
272
|
+
|
273
|
+
def define(node, in_sentence=false)
|
274
|
+
args = enumerate(node.arguments.names)
|
275
|
+
|
276
|
+
if @in_class
|
277
|
+
emit "\n\nA #{@in_class} can **#{node.name}**"
|
278
|
+
else
|
279
|
+
emit "\n\nLet's define a method: **#{node.name}**"
|
280
|
+
end
|
281
|
+
if node.arguments.names.empty?
|
282
|
+
emit "."
|
283
|
+
else
|
284
|
+
emit ", given a specific #{args}."
|
285
|
+
end
|
286
|
+
|
287
|
+
unless node.body.array.first.is_a?(Rubinius::AST::NilLiteral) && node.body.array.length == 1
|
288
|
+
@in_method = true
|
289
|
+
emit " This is described as follows: "
|
290
|
+
visit node.body, true
|
291
|
+
@in_method = false
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
def method_missing(m, *args, &block)
|
296
|
+
emit "(eerr I don't know how to explain `#{m}`)"
|
297
|
+
end
|
298
|
+
|
299
|
+
private
|
300
|
+
|
301
|
+
def enumerate(ary)
|
302
|
+
comma_joinable = ary[0..-2]
|
303
|
+
if comma_joinable.empty?
|
304
|
+
"#{ary[-1]}"
|
305
|
+
else
|
306
|
+
[comma_joined.join(', '), ary[-1]].join(' and ')
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def process_binary_operator(node, in_sentence)
|
311
|
+
operators = %w(+ - * / & | << < > >= <= == !=).map(&:to_sym)
|
312
|
+
return false unless operators.include?(node.name)
|
313
|
+
return false if node.arguments.array.length != 1
|
314
|
+
|
315
|
+
operand = node.arguments.array[0]
|
316
|
+
|
317
|
+
unless node.receiver.is_a?(Rubinius::AST::Self)
|
318
|
+
visit node.receiver
|
319
|
+
end
|
320
|
+
|
321
|
+
emit case node.name.to_s
|
322
|
+
when "+" then " plus "
|
323
|
+
when "-" then " minus "
|
324
|
+
when "*" then " times "
|
325
|
+
when "/" then " divided by "
|
326
|
+
when "<" then " is less than "
|
327
|
+
when ">" then " is greater than "
|
328
|
+
when ">=" then " is greater or equal than "
|
329
|
+
when "<=" then " is less or equal than "
|
330
|
+
when "==" then " is equal to "
|
331
|
+
when "!=" then " is different than "
|
332
|
+
end
|
333
|
+
|
334
|
+
visit operand, true
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require 'test_helper'
|
2
|
+
require 'explain/visitor'
|
3
|
+
|
4
|
+
module Explain
|
5
|
+
describe Visitor do
|
6
|
+
let(:code) { "a = 123".to_ast }
|
7
|
+
let(:visitor) { Visitor.new }
|
8
|
+
|
9
|
+
def self.compiles(code, expected)
|
10
|
+
it "explains #{code}" do
|
11
|
+
visitor.visit(code.to_ast)
|
12
|
+
visitor.output.must_equal(expected)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Basic types
|
17
|
+
compiles %q{123},
|
18
|
+
%q{the number 123}
|
19
|
+
compiles %q{12.3},
|
20
|
+
%q{the number 12.3}
|
21
|
+
compiles %q{:hey},
|
22
|
+
%q{the name `hey`}
|
23
|
+
compiles %q{"hey"},
|
24
|
+
%q{the word "hey"}
|
25
|
+
|
26
|
+
compiles %q{true},
|
27
|
+
%q{the truth}
|
28
|
+
compiles %q{false},
|
29
|
+
%q{the falsehood}
|
30
|
+
compiles %q{nil},
|
31
|
+
%q{the nothingness}
|
32
|
+
|
33
|
+
compiles %q{[]},
|
34
|
+
%q{an empty list}
|
35
|
+
compiles %q{["hey", 9.9, 123]},
|
36
|
+
%q{a list of the word "hey", the number 9.9 and the number 123}
|
37
|
+
compiles %q{{}},
|
38
|
+
%q{an empty dictionary}
|
39
|
+
compiles %q{{:foo => "bar", :baz => 123}},
|
40
|
+
%q{a dictionary where the name `foo` relates to the word "bar" and the name `baz` relates to the number 123}
|
41
|
+
|
42
|
+
# Assignments
|
43
|
+
compiles %q{a = 123},
|
44
|
+
%q{We define as `a` the number 123}
|
45
|
+
compiles %q{name = "John"},
|
46
|
+
%q{We define as `name` the word "John"}
|
47
|
+
|
48
|
+
compiles %q{def ret_name; name = "John"; name; end},
|
49
|
+
%Q{\n\nLet's define a method: **ret_name**. This is described as follows: we define as `name` the word "John". Finally we return what we previously defined as `name`.}
|
50
|
+
compiles %q{@name},
|
51
|
+
%q{its name}
|
52
|
+
compiles %q{@name = "John"},
|
53
|
+
%q{Its name will be the word "John"}
|
54
|
+
|
55
|
+
# Classes and modules
|
56
|
+
compiles %q{class Person; end},
|
57
|
+
%Q{Let's describe the general attributes and behavior of any Person.\nAnd with this we're done describing a Person.}
|
58
|
+
|
59
|
+
compiles %q{class Person
|
60
|
+
def walk(distance)
|
61
|
+
@position += distance
|
62
|
+
update_hunger 1
|
63
|
+
end
|
64
|
+
end},
|
65
|
+
%Q{Let's describe the general attributes and behavior of any Person.\n\nA Person can **walk**, given a specific distance. This is described as follows: its position will be its position plus what we previously defined as `distance`. Finally we return the result of calling **update_hunger** given the number 1..\nAnd with this we're done describing a Person.}
|
66
|
+
|
67
|
+
# Control flow
|
68
|
+
compiles %q{if true
|
69
|
+
false
|
70
|
+
else
|
71
|
+
:foo
|
72
|
+
end},
|
73
|
+
%Q{if the truth is truthy, then the falsehood -- else, the name `foo`}
|
74
|
+
|
75
|
+
compiles %q{number = 0
|
76
|
+
while number < 3
|
77
|
+
number += 1
|
78
|
+
end},
|
79
|
+
%Q{We define as `number` the number 0. while what we previously defined as `number` is less than the number 3, we define as `number` what we previously defined as `number` plus the number 1.}
|
80
|
+
|
81
|
+
compiles %q{number = 0
|
82
|
+
until number == 3
|
83
|
+
number += 1
|
84
|
+
end},
|
85
|
+
%Q{We define as `number` the number 0. until what we previously defined as `number` is equal to the number 3, we define as `number` what we previously defined as `number` plus the number 1.}
|
86
|
+
|
87
|
+
compiles %q{return 3},
|
88
|
+
%Q{We return the number 3}
|
89
|
+
end
|
90
|
+
end
|
data/test/test_helper.rb
ADDED
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: explain
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
prerelease:
|
5
|
+
version: 0.0.1
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Josep M. Bach
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-10-07 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Explain explains your Ruby code in natural language.
|
15
|
+
email:
|
16
|
+
- josep.m.bach@gmail.com
|
17
|
+
executables:
|
18
|
+
- explain
|
19
|
+
extensions: []
|
20
|
+
extra_rdoc_files: []
|
21
|
+
files:
|
22
|
+
- .gitignore
|
23
|
+
- .rvmrc
|
24
|
+
- Gemfile
|
25
|
+
- LICENSE.txt
|
26
|
+
- README.md
|
27
|
+
- Rakefile
|
28
|
+
- bin/explain
|
29
|
+
- examples/person.rb
|
30
|
+
- explain.gemspec
|
31
|
+
- lib/explain.rb
|
32
|
+
- lib/explain/version.rb
|
33
|
+
- lib/explain/visitor.rb
|
34
|
+
- test/explain/visitor_test.rb
|
35
|
+
- test/test_helper.rb
|
36
|
+
homepage: https://github.com/txus/explain
|
37
|
+
licenses: []
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ! '>='
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
none: false
|
48
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
49
|
+
requirements:
|
50
|
+
- - ! '>='
|
51
|
+
- !ruby/object:Gem::Version
|
52
|
+
version: '0'
|
53
|
+
none: false
|
54
|
+
requirements: []
|
55
|
+
rubyforge_project:
|
56
|
+
rubygems_version: 1.8.24
|
57
|
+
signing_key:
|
58
|
+
specification_version: 3
|
59
|
+
summary: Explain explains your Ruby code in natural language.
|
60
|
+
test_files:
|
61
|
+
- test/explain/visitor_test.rb
|
62
|
+
- test/test_helper.rb
|