guff 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +0 -0
- data/Manifest.txt +21 -0
- data/README.txt +3 -0
- data/Rakefile +54 -0
- data/lib/guff/java_source.rb +576 -0
- data/lib/guff/version.rb +9 -0
- data/lib/guff.rb +2 -0
- data/setup.rb +1585 -0
- data/test/java_source/annotation_declaration_test.rb +49 -0
- data/test/java_source/class_declaration_test.rb +55 -0
- data/test/java_source/field_declaration_test.rb +19 -0
- data/test/java_source/files/ClassWithAnnotatedFields.java +6 -0
- data/test/java_source/files/ClassWithAnnotatedMethods.java +32 -0
- data/test/java_source/files/ClassWithFields.java +10 -0
- data/test/java_source/files/ClassWithMethods.java +17 -0
- data/test/java_source/files/EmptyClass.java +4 -0
- data/test/java_source/files/GenericClass.java +4 -0
- data/test/java_source/method_declaration_test.rb +25 -0
- data/test/java_source/test_helper.rb +80 -0
- data/test/test_helper.rb +1 -0
- metadata +68 -0
data/CHANGELOG.txt
ADDED
File without changes
|
data/Manifest.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Rakefile
|
2
|
+
README.txt
|
3
|
+
CHANGELOG.txt
|
4
|
+
Manifest.txt
|
5
|
+
setup.rb
|
6
|
+
lib/guff/version.rb
|
7
|
+
lib/guff/java_source.rb
|
8
|
+
lib/guff.rb
|
9
|
+
test/test_helper.rb
|
10
|
+
test/java_source/files/ClassWithAnnotatedFields.java
|
11
|
+
test/java_source/files/ClassWithAnnotatedMethods.java
|
12
|
+
test/java_source/files/ClassWithFields.java
|
13
|
+
test/java_source/files/ClassWithMethods.java
|
14
|
+
test/java_source/files/EmptyClass.java
|
15
|
+
test/java_source/files/GenericClass.java
|
16
|
+
test/java_source/annotation_declaration_test.rb
|
17
|
+
test/java_source/class_declaration_test.rb
|
18
|
+
test/java_source/field_declaration_test.rb
|
19
|
+
test/java_source/method_declaration_test.rb
|
20
|
+
test/java_source/test_helper.rb
|
21
|
+
test/test_helper.rb
|
data/README.txt
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'rake/contrib/rubyforgepublisher'
|
9
|
+
require 'fileutils'
|
10
|
+
require 'hoe'
|
11
|
+
include FileUtils
|
12
|
+
require File.join(File.dirname(__FILE__), 'lib', 'guff', 'version')
|
13
|
+
|
14
|
+
AUTHOR = "Mike Hogan" # can also be an array of Authors
|
15
|
+
EMAIL = "me@mikehogan.net"
|
16
|
+
DESCRIPTION = "An API to describe and generate Java source code. The idea is to avoid writing all the 'guff' that is typical in java projects by generating it from more succinct ruby models."
|
17
|
+
GEM_NAME = "guff" # what ppl will type to install your gem
|
18
|
+
RUBYFORGE_PROJECT = "guff" # The unix name for your project
|
19
|
+
HOMEPATH = "http://#{RUBYFORGE_PROJECT}.rubyforge.org"
|
20
|
+
|
21
|
+
|
22
|
+
NAME = "guff"
|
23
|
+
REV = nil # UNCOMMENT IF REQUIRED: File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
24
|
+
VERS = ENV['VERSION'] || (Guff::VERSION::STRING + (REV ? ".#{REV}" : ""))
|
25
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
26
|
+
RDOC_OPTS = ['--quiet', '--title', "guff documentation",
|
27
|
+
"--opname", "index.html",
|
28
|
+
"--line-numbers",
|
29
|
+
"--main", "README",
|
30
|
+
"--inline-source"]
|
31
|
+
|
32
|
+
class Hoe
|
33
|
+
def extra_deps
|
34
|
+
@extra_deps.reject { |x| Array(x).first == 'hoe' }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Generate all the Rake tasks
|
39
|
+
# Run 'rake -T' to see list of generated tasks (from gem root directory)
|
40
|
+
hoe = Hoe.new(GEM_NAME, VERS) do |p|
|
41
|
+
p.author = AUTHOR
|
42
|
+
p.description = DESCRIPTION
|
43
|
+
p.email = EMAIL
|
44
|
+
p.summary = DESCRIPTION
|
45
|
+
p.url = HOMEPATH
|
46
|
+
p.rubyforge_name = RUBYFORGE_PROJECT if RUBYFORGE_PROJECT
|
47
|
+
p.test_globs = ["test/**/*_test.rb"]
|
48
|
+
p.clean_globs = CLEAN #An array of file patterns to delete on clean.
|
49
|
+
|
50
|
+
# == Optional
|
51
|
+
#p.changes - A description of the release's latest changes.
|
52
|
+
#p.extra_deps - An array of rubygem dependencies.
|
53
|
+
#p.spec_extras - A hash of extra values to set in the gemspec.
|
54
|
+
end
|
@@ -0,0 +1,576 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
|
3
|
+
module ArgumentsHelper
|
4
|
+
def ensure_array(a)
|
5
|
+
if (a.respond_to? :[])
|
6
|
+
return a
|
7
|
+
end
|
8
|
+
[a]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class String
|
13
|
+
def ends_with?(s)
|
14
|
+
reverse.index(s) == 0
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class Array
|
19
|
+
def each_joined(joiner)
|
20
|
+
last = size - 1
|
21
|
+
for i in 0..last
|
22
|
+
joiner.call if i > 0
|
23
|
+
yield self[i]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
module Guff
|
29
|
+
module JavaSource
|
30
|
+
|
31
|
+
module ClientSyntaxSupport
|
32
|
+
def annotation(type)
|
33
|
+
a = Annotation.new(type)
|
34
|
+
yield a if block_given?
|
35
|
+
a
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module AnnotationSupport
|
40
|
+
def add_annotation(type,value=nil)
|
41
|
+
annotation = Annotation.new(type,value)
|
42
|
+
@annotations << annotation
|
43
|
+
yield annotation if block_given?
|
44
|
+
self
|
45
|
+
end
|
46
|
+
|
47
|
+
def write_annotations_to(writer)
|
48
|
+
@annotations.each {|a|
|
49
|
+
a.write_to(writer)
|
50
|
+
writer.new_line
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
module ParameterSupport
|
56
|
+
def takes(name, type)
|
57
|
+
@parameters << Parameter.new(name, type)
|
58
|
+
self
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module ScopeSupport
|
63
|
+
def package_local
|
64
|
+
@scope = ''
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def protected
|
69
|
+
@scope = 'protected'
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def public
|
74
|
+
@scope = 'public'
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def private
|
79
|
+
@scope = 'private'
|
80
|
+
self
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
module ModifierSupport
|
85
|
+
def static
|
86
|
+
@modifiers << 'static'
|
87
|
+
self
|
88
|
+
end
|
89
|
+
|
90
|
+
def final
|
91
|
+
@modifiers << 'final'
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
def abstract
|
96
|
+
@modifiers << 'abstract'
|
97
|
+
self
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class UnnamedProperty
|
102
|
+
def initialize(value)
|
103
|
+
@value = value
|
104
|
+
end
|
105
|
+
|
106
|
+
def write_to(writer)
|
107
|
+
write_value(writer)
|
108
|
+
end
|
109
|
+
|
110
|
+
def write_value(writer)
|
111
|
+
@value.write_to(writer) if @value.respond_to? :write_to
|
112
|
+
writer.append(@value) unless @value.respond_to? :write_to
|
113
|
+
self
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
class Property < UnnamedProperty
|
118
|
+
def initialize(name, value)
|
119
|
+
super(value)
|
120
|
+
@name = name
|
121
|
+
end
|
122
|
+
|
123
|
+
def write_to(writer)
|
124
|
+
writer.append(@name).append("=")
|
125
|
+
write_value(writer)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
class Annotation
|
130
|
+
def initialize(type,value=nil)
|
131
|
+
@type = type
|
132
|
+
@properties = []
|
133
|
+
@properties << UnnamedProperty.new(value) unless value.nil?
|
134
|
+
end
|
135
|
+
|
136
|
+
def write_to(writer)
|
137
|
+
writer.append("@#{@type}")
|
138
|
+
write_properties_to(writer) unless @properties.empty?
|
139
|
+
end
|
140
|
+
|
141
|
+
def write_properties_to(writer)
|
142
|
+
writer.append("(").indent.new_line
|
143
|
+
joiner = proc {
|
144
|
+
writer.append(",").new_line
|
145
|
+
}
|
146
|
+
writer.append_all(@properties, joiner)
|
147
|
+
writer.outdent.new_line.append(")")
|
148
|
+
end
|
149
|
+
|
150
|
+
def add_property(name, value)
|
151
|
+
@properties << Property.new(name, value)
|
152
|
+
self
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
class Parameter
|
157
|
+
def initialize(n, t)
|
158
|
+
@name=n
|
159
|
+
@guff_type=t
|
160
|
+
end
|
161
|
+
|
162
|
+
def as_source
|
163
|
+
"#{guff_type} #{@name}"
|
164
|
+
end
|
165
|
+
|
166
|
+
def name
|
167
|
+
@name
|
168
|
+
end
|
169
|
+
|
170
|
+
def guff_type
|
171
|
+
@guff_type
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class Constructor
|
176
|
+
include ArgumentsHelper, ParameterSupport, AnnotationSupport
|
177
|
+
|
178
|
+
def initialize(name)
|
179
|
+
@name = name
|
180
|
+
@parameters = []
|
181
|
+
@body = Body.default_body
|
182
|
+
@annotations = []
|
183
|
+
end
|
184
|
+
|
185
|
+
def body
|
186
|
+
@body = Body.new
|
187
|
+
yield @body
|
188
|
+
self
|
189
|
+
end
|
190
|
+
|
191
|
+
def parameters
|
192
|
+
result = []
|
193
|
+
@parameters.each {|p|
|
194
|
+
result << p.as_source
|
195
|
+
}
|
196
|
+
result.join(',')
|
197
|
+
end
|
198
|
+
|
199
|
+
def write_to(writer)
|
200
|
+
writer.ensure_member_separation_line
|
201
|
+
write_annotations_to(writer)
|
202
|
+
write_declaration(writer)
|
203
|
+
@body.write_to(writer) unless @body.nil?
|
204
|
+
end
|
205
|
+
|
206
|
+
def write_declaration(writer)
|
207
|
+
writer.append("public #{@name}(" ).append(parameters).append(")")
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
module BodyBits
|
212
|
+
class Line
|
213
|
+
def initialize(l)
|
214
|
+
@line = l
|
215
|
+
end
|
216
|
+
|
217
|
+
def write_to(writer)
|
218
|
+
writer.line(@line)
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
class Text
|
223
|
+
def initialize(t)
|
224
|
+
@text = t
|
225
|
+
end
|
226
|
+
|
227
|
+
def write_to(writer)
|
228
|
+
writer.append(@text)
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
class NestedBody
|
233
|
+
def initialize(body)
|
234
|
+
@body = body
|
235
|
+
end
|
236
|
+
|
237
|
+
def write_to(writer)
|
238
|
+
@body.write_to(writer)
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
243
|
+
class Body
|
244
|
+
def self.default_body
|
245
|
+
EmptyBody.new
|
246
|
+
end
|
247
|
+
|
248
|
+
def initialize
|
249
|
+
@bits = []
|
250
|
+
end
|
251
|
+
|
252
|
+
def line(l)
|
253
|
+
@bits << BodyBits::Line.new(l)
|
254
|
+
self
|
255
|
+
end
|
256
|
+
|
257
|
+
def append(t)
|
258
|
+
@bits << BodyBits::Text.new(t)
|
259
|
+
self
|
260
|
+
end
|
261
|
+
|
262
|
+
def body
|
263
|
+
nested = Body.new
|
264
|
+
@bits << BodyBits::NestedBody.new(nested)
|
265
|
+
yield nested
|
266
|
+
self
|
267
|
+
end
|
268
|
+
|
269
|
+
def write_to(writer)
|
270
|
+
writer.open_brace
|
271
|
+
@bits.each {|bit|
|
272
|
+
bit.write_to(writer)
|
273
|
+
}
|
274
|
+
writer.close_brace
|
275
|
+
end
|
276
|
+
end
|
277
|
+
|
278
|
+
class EmptyBody < Body
|
279
|
+
def write_to(writer)
|
280
|
+
writer.semi_colon.new_line
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
class Method < Constructor
|
285
|
+
include ScopeSupport, ModifierSupport, AnnotationSupport
|
286
|
+
|
287
|
+
def initialize(name)
|
288
|
+
super(name)
|
289
|
+
@return_type = 'void'
|
290
|
+
@modifiers = []
|
291
|
+
@scope = 'public'
|
292
|
+
end
|
293
|
+
|
294
|
+
def returns(t)
|
295
|
+
@return_type = t
|
296
|
+
self
|
297
|
+
end
|
298
|
+
|
299
|
+
def returns_list_of(t)
|
300
|
+
@return_type= "List<#{t}>"
|
301
|
+
self
|
302
|
+
end
|
303
|
+
|
304
|
+
def return_type
|
305
|
+
@return_type
|
306
|
+
end
|
307
|
+
|
308
|
+
def write_declaration(writer)
|
309
|
+
writer.word(@scope).words(@modifiers).word(@return_type).word("#{@name}(").append(parameters).append(")")
|
310
|
+
end
|
311
|
+
end
|
312
|
+
|
313
|
+
class Field
|
314
|
+
include ScopeSupport, ModifierSupport, AnnotationSupport
|
315
|
+
def initialize(name, type)
|
316
|
+
@name = name
|
317
|
+
@type = type
|
318
|
+
@scope = 'private'
|
319
|
+
@modifiers = []
|
320
|
+
@initializer = ''
|
321
|
+
@annotations = []
|
322
|
+
end
|
323
|
+
|
324
|
+
def write_to(writer)
|
325
|
+
write_annotations_to(writer)
|
326
|
+
writer.word(@scope).words(@modifiers).word(@type).word(@name).word(@initializer).semi_colon.new_line
|
327
|
+
end
|
328
|
+
|
329
|
+
def initial(i)
|
330
|
+
@initializer = "= #{i}"
|
331
|
+
self
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
class Class
|
336
|
+
include ScopeSupport, ModifierSupport, AnnotationSupport
|
337
|
+
|
338
|
+
def initialize(name, package, source_file)
|
339
|
+
@name = name;
|
340
|
+
@package = package
|
341
|
+
@modifiers = ['public']
|
342
|
+
@extends = nil
|
343
|
+
@instance_methods = []
|
344
|
+
@fields = []
|
345
|
+
@constructors = []
|
346
|
+
@source_file=source_file
|
347
|
+
@genericized_using = ''
|
348
|
+
@interfaces = nil
|
349
|
+
@annotations = []
|
350
|
+
@implements = nil
|
351
|
+
end
|
352
|
+
|
353
|
+
def add_field(name, type)
|
354
|
+
f = Field.new(name, type)
|
355
|
+
@fields << f
|
356
|
+
f
|
357
|
+
end
|
358
|
+
|
359
|
+
def genericized_using(g)
|
360
|
+
@genericized_using = g
|
361
|
+
self
|
362
|
+
end
|
363
|
+
|
364
|
+
def name
|
365
|
+
@name
|
366
|
+
end
|
367
|
+
|
368
|
+
def fully_qualified_name
|
369
|
+
"#{@package}.#{@name}"
|
370
|
+
end
|
371
|
+
|
372
|
+
def extends(d)
|
373
|
+
@extends = d
|
374
|
+
self
|
375
|
+
end
|
376
|
+
|
377
|
+
def implements(*interfaces)
|
378
|
+
@implements = interfaces
|
379
|
+
self
|
380
|
+
end
|
381
|
+
|
382
|
+
def add_method(name)
|
383
|
+
result = Method.new(name)
|
384
|
+
@instance_methods << result
|
385
|
+
result
|
386
|
+
end
|
387
|
+
|
388
|
+
def add_constructor
|
389
|
+
result = Constructor.new(@name)
|
390
|
+
@constructors << result
|
391
|
+
result
|
392
|
+
end
|
393
|
+
|
394
|
+
def write_to(writer)
|
395
|
+
write_annotations_to(writer)
|
396
|
+
writer.words(@modifiers).word("class").word(@name).append(@genericized_using).word_with_prefix('extends', @extends).list_with_prefix('implements', @implements).open_brace
|
397
|
+
|
398
|
+
@fields.each {|f|
|
399
|
+
f.write_to(writer)
|
400
|
+
}
|
401
|
+
@constructors.each {|c|
|
402
|
+
c.write_to(writer)
|
403
|
+
}
|
404
|
+
@instance_methods.each {|m|
|
405
|
+
m.write_to(writer)
|
406
|
+
}
|
407
|
+
writer.close_brace
|
408
|
+
end
|
409
|
+
end
|
410
|
+
|
411
|
+
class SourceFile
|
412
|
+
def initialize
|
413
|
+
@package = 'set the package'
|
414
|
+
@classes = []
|
415
|
+
end
|
416
|
+
|
417
|
+
def save_in(root)
|
418
|
+
writer = Printer.new
|
419
|
+
write_to(writer)
|
420
|
+
directory = root << "/" + package_as_directory
|
421
|
+
if (!FileTest.exist?(directory))
|
422
|
+
FileUtils.mkdir_p(directory)
|
423
|
+
end
|
424
|
+
filename = directory << "/" << @name << ".java"
|
425
|
+
file = File.new(filename, "w")
|
426
|
+
file.write(writer.contents)
|
427
|
+
file.close
|
428
|
+
filename
|
429
|
+
end
|
430
|
+
|
431
|
+
def package_as_directory
|
432
|
+
@package.split('.').join('/')
|
433
|
+
end
|
434
|
+
|
435
|
+
def package(p)
|
436
|
+
@package=p
|
437
|
+
self
|
438
|
+
end
|
439
|
+
|
440
|
+
def begin_class(name)
|
441
|
+
@name = name
|
442
|
+
result = Class.new(name, @package, self)
|
443
|
+
@classes << result
|
444
|
+
yield result if block_given?
|
445
|
+
self
|
446
|
+
end
|
447
|
+
|
448
|
+
def write_to(writer)
|
449
|
+
writer.line(package_def).line
|
450
|
+
@classes.each {|c|
|
451
|
+
c.write_to(writer)
|
452
|
+
}
|
453
|
+
writer
|
454
|
+
end
|
455
|
+
|
456
|
+
def package_def
|
457
|
+
"package #{@package};"
|
458
|
+
end
|
459
|
+
end
|
460
|
+
|
461
|
+
class Printer
|
462
|
+
def initialize
|
463
|
+
@text=""
|
464
|
+
@indent=0
|
465
|
+
@current_line = nil
|
466
|
+
end
|
467
|
+
|
468
|
+
def line(s="")
|
469
|
+
append(s)
|
470
|
+
new_line
|
471
|
+
end
|
472
|
+
|
473
|
+
def append(s)
|
474
|
+
if (@current_line.nil?)
|
475
|
+
@current_line = indent_text
|
476
|
+
end
|
477
|
+
@current_line << s.to_s
|
478
|
+
self
|
479
|
+
end
|
480
|
+
|
481
|
+
def append_all(writers, joiner)
|
482
|
+
writers.each_joined(joiner) {|w|
|
483
|
+
w.write_to(self)
|
484
|
+
}
|
485
|
+
end
|
486
|
+
|
487
|
+
def word_with_prefix(prefix, w)
|
488
|
+
if (w.to_s.size>0)
|
489
|
+
word(prefix).word(w)
|
490
|
+
end
|
491
|
+
self
|
492
|
+
end
|
493
|
+
|
494
|
+
def words_with_prefix(prefix, ww)
|
495
|
+
if (ww.nil? == false && ww.size > 0)
|
496
|
+
word(prefix).words(ww)
|
497
|
+
end
|
498
|
+
self
|
499
|
+
end
|
500
|
+
|
501
|
+
def list_with_prefix(prefix, list, joiner=',')
|
502
|
+
if (list.nil? == false && list.size > 0)
|
503
|
+
word(prefix).word(list.join(joiner))
|
504
|
+
end
|
505
|
+
self
|
506
|
+
end
|
507
|
+
|
508
|
+
def ensure_member_separation_line
|
509
|
+
if (!@text.ends_with?("\n\n"))
|
510
|
+
new_line
|
511
|
+
end
|
512
|
+
end
|
513
|
+
|
514
|
+
def word(w)
|
515
|
+
if (w.to_s.size > 0)
|
516
|
+
if (!@current_line.nil? && !@text.ends_with?(' '))
|
517
|
+
w = " #{w}"
|
518
|
+
end
|
519
|
+
append(w)
|
520
|
+
end
|
521
|
+
self
|
522
|
+
end
|
523
|
+
|
524
|
+
def words(ww)
|
525
|
+
ww.each {|w|
|
526
|
+
word(w)
|
527
|
+
}
|
528
|
+
self
|
529
|
+
end
|
530
|
+
|
531
|
+
def semi_colon
|
532
|
+
append(';')
|
533
|
+
end
|
534
|
+
|
535
|
+
def indent_text
|
536
|
+
result = ""
|
537
|
+
@indent.times {
|
538
|
+
result << " "
|
539
|
+
}
|
540
|
+
result
|
541
|
+
end
|
542
|
+
|
543
|
+
|
544
|
+
def new_line
|
545
|
+
@text << @current_line unless @current_line.nil?
|
546
|
+
@text << "\n"
|
547
|
+
@current_line=nil
|
548
|
+
self
|
549
|
+
end
|
550
|
+
|
551
|
+
def indent
|
552
|
+
@indent += 1
|
553
|
+
self
|
554
|
+
end
|
555
|
+
|
556
|
+
def outdent
|
557
|
+
@indent -= 1
|
558
|
+
self
|
559
|
+
end
|
560
|
+
|
561
|
+
def open_brace
|
562
|
+
line("{")
|
563
|
+
indent
|
564
|
+
end
|
565
|
+
|
566
|
+
def close_brace
|
567
|
+
outdent
|
568
|
+
line("}")
|
569
|
+
end
|
570
|
+
|
571
|
+
def contents
|
572
|
+
@text
|
573
|
+
end
|
574
|
+
end
|
575
|
+
end
|
576
|
+
end
|
data/lib/guff/version.rb
ADDED
data/lib/guff.rb
ADDED