gravitext-devtools 1.0.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.
- data/History.rdoc +2 -0
- data/Manifest.txt +18 -0
- data/README.rdoc +31 -0
- data/Rakefile +29 -0
- data/bin/gt-cleanws +227 -0
- data/bin/gt-header +22 -0
- data/bin/gt-manifest +22 -0
- data/lib/gravitext-devtools.rb +131 -0
- data/lib/gravitext-devtools/base.rb +21 -0
- data/lib/gravitext-devtools/header_writer.rb +298 -0
- data/lib/gravitext-devtools/manifest_writer.rb +135 -0
- data/templates/format/format.java +5 -0
- data/templates/format/format.rb +5 -0
- data/templates/format/format.txt +3 -0
- data/templates/format/format.xml +5 -0
- data/templates/license/apache +13 -0
- data/templates/license/bsd +27 -0
- data/templates/license/mit +21 -0
- metadata +93 -0
data/History.rdoc
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
History.rdoc
|
2
|
+
Manifest.txt
|
3
|
+
README.rdoc
|
4
|
+
Rakefile
|
5
|
+
bin/gt-cleanws
|
6
|
+
bin/gt-header
|
7
|
+
bin/gt-manifest
|
8
|
+
lib/gravitext-devtools/base.rb
|
9
|
+
lib/gravitext-devtools.rb
|
10
|
+
lib/gravitext-devtools/header_writer.rb
|
11
|
+
lib/gravitext-devtools/manifest_writer.rb
|
12
|
+
templates/format/format.java
|
13
|
+
templates/format/format.rb
|
14
|
+
templates/format/format.txt
|
15
|
+
templates/format/format.xml
|
16
|
+
templates/license/apache
|
17
|
+
templates/license/bsd
|
18
|
+
templates/license/mit
|
data/README.rdoc
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
= gravitext-devtools
|
2
|
+
|
3
|
+
* http://github.com/dekellum/gravitext-devtools
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
A collection of development support tools.
|
8
|
+
|
9
|
+
gt-manifest:: Maintain manifest from git ls-files minus exclusions.
|
10
|
+
gt-cleanws:: Cleanup whitespace in files.
|
11
|
+
gt-header:: Add and maintain file license headers.
|
12
|
+
|
13
|
+
== Synopsis
|
14
|
+
|
15
|
+
[TBD]
|
16
|
+
|
17
|
+
== License
|
18
|
+
|
19
|
+
Copyright (c) 2008-2010 David Kellum
|
20
|
+
|
21
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you
|
22
|
+
may not use this file except in compliance with the License. You
|
23
|
+
may obtain a copy of the License at:
|
24
|
+
|
25
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
26
|
+
|
27
|
+
Unless required by applicable law or agreed to in writing, software
|
28
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
29
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
30
|
+
implied. See the License for the specific language governing
|
31
|
+
permissions and limitations under the License.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
$LOAD_PATH << './lib'
|
4
|
+
require 'gravitext-devtools/base'
|
5
|
+
|
6
|
+
require 'rubygems'
|
7
|
+
gem 'rjack-tarpit', '~> 1.2.1'
|
8
|
+
require 'rjack-tarpit'
|
9
|
+
|
10
|
+
t = RJack::TarPit.new( 'gravitext-devtools', Gravitext::DevTools::VERSION )
|
11
|
+
|
12
|
+
t.specify do |h|
|
13
|
+
h.developer( 'David Kellum', 'dek-oss@gravitext.com' )
|
14
|
+
end
|
15
|
+
|
16
|
+
task :check_history_version do
|
17
|
+
t.test_line_match( 'History.rdoc', /^==/, / #{ t.version } / )
|
18
|
+
end
|
19
|
+
task :check_history_date do
|
20
|
+
t.test_line_match( 'History.rdoc', /^==/, /\([0-9\-]+\)$/ )
|
21
|
+
end
|
22
|
+
|
23
|
+
task :gem => [ :check_history_version, ]
|
24
|
+
task :tag => [ :check_history_version, :check_history_date ]
|
25
|
+
task :push => [ :check_history_version, :check_history_date ]
|
26
|
+
|
27
|
+
task :test #noop
|
28
|
+
|
29
|
+
t.define_tasks
|
data/bin/gt-cleanws
ADDED
@@ -0,0 +1,227 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2008-2010 David Kellum
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
6
|
+
# may not use this file except in compliance with the License. You may
|
7
|
+
# obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
14
|
+
# implied. See the License for the specific language governing
|
15
|
+
# permissions and limitations under the License.
|
16
|
+
#++
|
17
|
+
|
18
|
+
require 'fileutils'
|
19
|
+
require 'optparse'
|
20
|
+
require 'stringio'
|
21
|
+
|
22
|
+
require 'rubygems'
|
23
|
+
require 'rainbow'
|
24
|
+
|
25
|
+
class CleanWS
|
26
|
+
include FileUtils
|
27
|
+
|
28
|
+
class NullIO
|
29
|
+
def puts(*a)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
CMAP = {}
|
34
|
+
|
35
|
+
def self.mk_tag( name, color )
|
36
|
+
tag = name.foreground( color )
|
37
|
+
CMAP[ tag ] = color
|
38
|
+
tag
|
39
|
+
end
|
40
|
+
|
41
|
+
CLEAN = mk_tag( "CLEAN", :green )
|
42
|
+
DIRTY = mk_tag( "DIRTY", :red )
|
43
|
+
WROTE = mk_tag( "WROTE", :red )
|
44
|
+
FIXED = mk_tag( "FIXED", :yellow )
|
45
|
+
NOTXT = mk_tag( "NOTXT", :blue )
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@do_write = false
|
49
|
+
@verbose = false
|
50
|
+
@show_clean = true
|
51
|
+
@fout = NullIO.new
|
52
|
+
@git_ls = false
|
53
|
+
@log = $stdout
|
54
|
+
@llines = 0
|
55
|
+
end
|
56
|
+
|
57
|
+
def parse_flags( args )
|
58
|
+
opts = OptionParser.new do |opts|
|
59
|
+
opts.banner = "Usage: cleanws [-w | -t] file ..."
|
60
|
+
opts.on( "-w", "--write", "Update file in place") do
|
61
|
+
@do_write = true
|
62
|
+
end
|
63
|
+
opts.on( "-v", "--verbose",
|
64
|
+
"Output filtered files and verbose logging.") do
|
65
|
+
@fout = $stdout
|
66
|
+
log = $stderr
|
67
|
+
@verbose = true
|
68
|
+
end
|
69
|
+
opts.on( "-d", "--dirty", "Show dirty status only.") do
|
70
|
+
@show_clean = false
|
71
|
+
end
|
72
|
+
opts.on( "-g", "--git-updates",
|
73
|
+
"Get input files from modified/untracked git working tree" ) do
|
74
|
+
@git_ls = "-m -o --exclude-standard"
|
75
|
+
end
|
76
|
+
opts.on( "-G", "--git-cached",
|
77
|
+
"Get input files from tracked/cached git files" ) do
|
78
|
+
@git_ls = "-c"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
opts.parse!( args )
|
82
|
+
end
|
83
|
+
|
84
|
+
def main( args )
|
85
|
+
|
86
|
+
parse_flags( args )
|
87
|
+
|
88
|
+
if @git_ls
|
89
|
+
gcmd = [ 'git ls-files', @git_ls, args ].flatten.join(' ')
|
90
|
+
@log.puts gcmd if @verbose
|
91
|
+
args = IO.popen( gcmd ).readlines.map { |f| f.strip }
|
92
|
+
args.reject! { |f| f =~ /\.ws(~|\.tmp)$/ }
|
93
|
+
end
|
94
|
+
|
95
|
+
args.sort!
|
96
|
+
|
97
|
+
ftypes = check_file_types( args )
|
98
|
+
|
99
|
+
any_dirty = false
|
100
|
+
|
101
|
+
ftypes.each do |ifile, type|
|
102
|
+
d = process( ifile, type )
|
103
|
+
any_dirty ||= d
|
104
|
+
end
|
105
|
+
|
106
|
+
header if @llines > 20
|
107
|
+
exit( any_dirty ? 1 : 0 )
|
108
|
+
end
|
109
|
+
|
110
|
+
def check_file_types( files )
|
111
|
+
fmap = {}
|
112
|
+
# 1: keep only regular files in fmap
|
113
|
+
files.each do |f|
|
114
|
+
if File.file?( f ) && ! File.symlink?( f )
|
115
|
+
fmap[f] = :file
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# 2: Use UNIX 'file' command to test for text files
|
120
|
+
unless fmap.empty?
|
121
|
+
IO.popen( "file " + fmap.keys.join( ' ' ) ) do |outf|
|
122
|
+
outf.each do |line|
|
123
|
+
# <filename>: <type string with /text/ or not>
|
124
|
+
if line =~ /^(.+):(.+)$/
|
125
|
+
fmap[ $1 ] = ( ( $2 =~ /text/ ) ? :text : :binary )
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
fmap.sort
|
132
|
+
end
|
133
|
+
|
134
|
+
def header
|
135
|
+
@log.puts( "%5s %4s %4s %4s %4s %s" %
|
136
|
+
[ "", "WS", "BLNK", "TABS", "LINE", "FILE" ] )
|
137
|
+
end
|
138
|
+
|
139
|
+
def process( ifile, type )
|
140
|
+
@log.puts( "CLEAN f: #{ifile} :::" ) if @verbose
|
141
|
+
|
142
|
+
ofile = nil
|
143
|
+
|
144
|
+
chg, blank, tabs, total =
|
145
|
+
if type == :text
|
146
|
+
ofile = ifile + ".ws.tmp"
|
147
|
+
|
148
|
+
fout = @do_write ? open( ofile, 'w' ) : @fout
|
149
|
+
|
150
|
+
result = open( ifile, 'r' ) do |fin|
|
151
|
+
filter( fin, fout )
|
152
|
+
end
|
153
|
+
|
154
|
+
fout.close if @do_write
|
155
|
+
result
|
156
|
+
else
|
157
|
+
[0,0,0,0]
|
158
|
+
end
|
159
|
+
|
160
|
+
change = ( ( chg + blank ) > 0 )
|
161
|
+
dirty = change || ( tabs > 0 )
|
162
|
+
|
163
|
+
state = if dirty
|
164
|
+
if @do_write
|
165
|
+
( tabs == 0 ) ? FIXED : WROTE
|
166
|
+
else
|
167
|
+
DIRTY
|
168
|
+
end
|
169
|
+
else
|
170
|
+
( type == :text ) ? CLEAN : NOTXT
|
171
|
+
end
|
172
|
+
|
173
|
+
if dirty || @show_clean
|
174
|
+
header if @llines == 0
|
175
|
+
@log.puts( "%s %4d %4d %4d %4d %s" %
|
176
|
+
[ state, chg, blank, tabs, total,
|
177
|
+
ifile.dup.foreground( CMAP[ state ] ) ] )
|
178
|
+
@llines += 1
|
179
|
+
end
|
180
|
+
|
181
|
+
if @do_write && ofile
|
182
|
+
if change
|
183
|
+
cp( ifile, ifile + ".ws~", :preserve => true )
|
184
|
+
cp( ofile, ifile )
|
185
|
+
end
|
186
|
+
rm( ofile )
|
187
|
+
end
|
188
|
+
dirty
|
189
|
+
end
|
190
|
+
|
191
|
+
# Filters files fin to fout
|
192
|
+
# All whitespace at end of line is replaced with '\n'
|
193
|
+
# Only one blank line allowed consecutively.
|
194
|
+
# Last non-empty line is terminated with '\n'
|
195
|
+
# No trailing additional lines in file.
|
196
|
+
def filter( fin, fout )
|
197
|
+
dropped_lines = 0
|
198
|
+
changed_lines = 0
|
199
|
+
tab_lines = 0
|
200
|
+
empty_lines = 0
|
201
|
+
total = 0
|
202
|
+
while( line = fin.gets )
|
203
|
+
total += 1
|
204
|
+
lstr = line.rstrip
|
205
|
+
|
206
|
+
changed_lines += 1 if ( line != ( lstr + "\n" ) )
|
207
|
+
|
208
|
+
if lstr.empty?
|
209
|
+
empty_lines += 1
|
210
|
+
else
|
211
|
+
if empty_lines > 0
|
212
|
+
fout.puts
|
213
|
+
dropped_lines += empty_lines - 1
|
214
|
+
empty_lines = 0
|
215
|
+
end
|
216
|
+
tab_lines += 1 if lstr =~ /\t/
|
217
|
+
# line.gsub!( /\t/, " " )
|
218
|
+
fout.puts lstr
|
219
|
+
end
|
220
|
+
end
|
221
|
+
dropped_lines += empty_lines
|
222
|
+
[ changed_lines, dropped_lines, tab_lines, total ]
|
223
|
+
end
|
224
|
+
|
225
|
+
end
|
226
|
+
|
227
|
+
CleanWS.new.main( ARGV )
|
data/bin/gt-header
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2008-2010 David Kellum
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
6
|
+
# may not use this file except in compliance with the License. You may
|
7
|
+
# obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
14
|
+
# implied. See the License for the specific language governing
|
15
|
+
# permissions and limitations under the License.
|
16
|
+
#++
|
17
|
+
|
18
|
+
$LOAD_PATH.unshift File.join( File.dirname( __FILE__ ), "..", "lib" )
|
19
|
+
|
20
|
+
require 'gravitext-devtools/header_writer'
|
21
|
+
|
22
|
+
Gravitext::DevTools::HeaderWriter.new.run
|
data/bin/gt-manifest
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#--
|
3
|
+
# Copyright (c) 2008-2010 David Kellum
|
4
|
+
#
|
5
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
6
|
+
# may not use this file except in compliance with the License. You may
|
7
|
+
# obtain a copy of the License at
|
8
|
+
#
|
9
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
10
|
+
#
|
11
|
+
# Unless required by applicable law or agreed to in writing, software
|
12
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
13
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
14
|
+
# implied. See the License for the specific language governing
|
15
|
+
# permissions and limitations under the License.
|
16
|
+
#++
|
17
|
+
|
18
|
+
$LOAD_PATH.unshift File.join( File.dirname( __FILE__ ), "..", "lib" )
|
19
|
+
|
20
|
+
require 'gravitext-devtools/manifest_writer'
|
21
|
+
|
22
|
+
Gravitext::DevTools::ManifestWriter.new.run
|
@@ -0,0 +1,131 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008-2010 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'gravitext-devtools/base.rb'
|
18
|
+
require 'optparse'
|
19
|
+
|
20
|
+
module Gravitext
|
21
|
+
module DevTools
|
22
|
+
|
23
|
+
def self.configure
|
24
|
+
yield Gravitext::DevTools::Config
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.load_config_from_pwd
|
28
|
+
count = 0
|
29
|
+
pwd = File.expand_path( Dir.pwd )
|
30
|
+
while( File.directory?( pwd ) )
|
31
|
+
cfile = File.join( pwd, '.gt-config' )
|
32
|
+
if File.exist?( cfile )
|
33
|
+
Config::load_config( cfile )
|
34
|
+
break
|
35
|
+
end
|
36
|
+
break if File.exist?( File.join( pwd, '.git' ) )
|
37
|
+
pwd = File.dirname( pwd )
|
38
|
+
break if ( count += 1 ) > 4
|
39
|
+
break if pwd == '/'
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Config
|
44
|
+
|
45
|
+
def self.load_config( file )
|
46
|
+
puts "Loading config #{file}."
|
47
|
+
load file
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.method_missing( method, *arguments, &block )
|
51
|
+
ccall = caller[0]
|
52
|
+
puts "Method %s from %s not defined, ignored" % [ method, ccall ]
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class GitFileLister
|
57
|
+
|
58
|
+
# Exclusions to the list expressed in various ways
|
59
|
+
# Array<Regexp|Proc|String>
|
60
|
+
attr_accessor :exclusions
|
61
|
+
|
62
|
+
# Flags for use in call to git ls-files
|
63
|
+
# Array<~to_s>
|
64
|
+
attr_accessor :git_flags
|
65
|
+
|
66
|
+
# Additional files to add to files list
|
67
|
+
# Array<~to_s>
|
68
|
+
attr_accessor :extra_files
|
69
|
+
|
70
|
+
def initialize
|
71
|
+
@args = []
|
72
|
+
@files = nil
|
73
|
+
@exclusions = [ %r{(^|/).gitignore$}, # gt-manifest, gt-header
|
74
|
+
%r{(^|/).gt-config$}, # gt-manifest, (gt-header)
|
75
|
+
%r{(^|/)src(/|$)}, # gt-manifest
|
76
|
+
'lib/**/*.jar', # all
|
77
|
+
'Manifest.static' ] # gt-manifest, gt-header
|
78
|
+
@git_flags = []
|
79
|
+
@extra_files = []
|
80
|
+
end
|
81
|
+
|
82
|
+
def parse_options( args = ARGV, &block )
|
83
|
+
opts = OptionParser.new do |opts|
|
84
|
+
opts.on( "-g", "--git-updates",
|
85
|
+
"Include files from modified/untracked git working tree" ) do
|
86
|
+
@git_flags += %w[ -m -o --exclude-standard ]
|
87
|
+
end
|
88
|
+
block.call( opts ) if block
|
89
|
+
end
|
90
|
+
@args = opts.parse( args )
|
91
|
+
end
|
92
|
+
|
93
|
+
def files
|
94
|
+
@files ||= generate_list
|
95
|
+
end
|
96
|
+
|
97
|
+
def generate_list( args = @args )
|
98
|
+
|
99
|
+
files, dirs = args.partition { |a| File.file?( a ) }
|
100
|
+
|
101
|
+
if files.empty? || ! dirs.empty?
|
102
|
+
gcmd = [ 'git', 'ls-files', @git_flags, dirs ].flatten.compact.join( ' ' )
|
103
|
+
files += IO.popen( gcmd ) { |inp| inp.readlines }
|
104
|
+
end
|
105
|
+
|
106
|
+
files += extra_files
|
107
|
+
|
108
|
+
files.map! { |f| f.strip }
|
109
|
+
files.uniq!
|
110
|
+
files.reject { |f| exclude?( f ) }
|
111
|
+
end
|
112
|
+
|
113
|
+
def exclude?( fname )
|
114
|
+
@exclusions.any? do |ex|
|
115
|
+
case ex
|
116
|
+
when Proc
|
117
|
+
ex.call( fname )
|
118
|
+
when Regexp
|
119
|
+
fname =~ ex
|
120
|
+
when /[*?]/
|
121
|
+
File.fnmatch?( ex, fname, File::FNM_PATHNAME )
|
122
|
+
else
|
123
|
+
fname == ex
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008-2010 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
module Gravitext
|
18
|
+
module DevTools
|
19
|
+
VERSION = "1.0.0"
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,298 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008-2010 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'gravitext-devtools'
|
18
|
+
require 'fileutils'
|
19
|
+
require 'erb'
|
20
|
+
require 'rubygems'
|
21
|
+
require 'rainbow'
|
22
|
+
|
23
|
+
module Gravitext
|
24
|
+
module DevTools
|
25
|
+
|
26
|
+
module Config
|
27
|
+
def self.header
|
28
|
+
hw = HeaderWriter.instance
|
29
|
+
raise "HeaderWriter not initialized" unless hw
|
30
|
+
yield hw
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class HeaderWriter
|
35
|
+
include FileUtils
|
36
|
+
|
37
|
+
attr_accessor :verbose
|
38
|
+
attr_accessor :holder
|
39
|
+
attr_accessor :inception
|
40
|
+
attr_accessor :license
|
41
|
+
attr_accessor :exclusions
|
42
|
+
|
43
|
+
class << self
|
44
|
+
attr_accessor :instance
|
45
|
+
end
|
46
|
+
|
47
|
+
def initialize
|
48
|
+
@verbose = false
|
49
|
+
@do_write = false
|
50
|
+
@license = :apache
|
51
|
+
@inception = Time.now.year
|
52
|
+
|
53
|
+
@git_lister = GitFileLister.new
|
54
|
+
|
55
|
+
@exclusions = [ '**/.gitignore',
|
56
|
+
'**/.gt-config',
|
57
|
+
'**/History.rdoc',
|
58
|
+
'**/History.txt',
|
59
|
+
'**/Manifest.static',
|
60
|
+
'**/Manifest.txt',
|
61
|
+
'**/*.jar',
|
62
|
+
'**/Rakefile',
|
63
|
+
'**/pom.xml',
|
64
|
+
'**/assembly.xml']
|
65
|
+
|
66
|
+
@cached_header = {}
|
67
|
+
|
68
|
+
HeaderWriter.instance = self # The last created
|
69
|
+
end
|
70
|
+
|
71
|
+
def parse_options( args = ARGV )
|
72
|
+
|
73
|
+
@git_lister.parse_options( args ) do |opts|
|
74
|
+
opts.banner = "Usage: gt-manifest [dir|file] ..."
|
75
|
+
opts.on( "-v", "--verbose",
|
76
|
+
"Output full manifest details" ) do
|
77
|
+
@verbose = true
|
78
|
+
end
|
79
|
+
opts.on( "-w", "--write",
|
80
|
+
"Write headers if needed" ) do
|
81
|
+
@do_write = true
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def run( args = ARGV )
|
88
|
+
|
89
|
+
parse_options( args )
|
90
|
+
|
91
|
+
Gravitext::DevTools.load_config_from_pwd
|
92
|
+
|
93
|
+
@git_lister.exclusions = @exclusions
|
94
|
+
# puts @exclusions.inspect
|
95
|
+
|
96
|
+
files = @git_lister.files
|
97
|
+
|
98
|
+
# puts cached_header( :rb )
|
99
|
+
# exit
|
100
|
+
|
101
|
+
files.each do |fname|
|
102
|
+
HeaderProcessor.new( fname, @do_write ).process
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
TDIR = File.expand_path( File.join( File.dirname( __FILE__ ),
|
107
|
+
'..', '..', 'templates' ) )
|
108
|
+
|
109
|
+
def cached_header( format )
|
110
|
+
@cached_header[ format ] ||= gen_header( format )
|
111
|
+
end
|
112
|
+
|
113
|
+
def gen_header( format )
|
114
|
+
efile = File.join( TDIR, 'format', "format.#{format}" )
|
115
|
+
license = cached_license
|
116
|
+
expand( IO.read( efile ), binding )
|
117
|
+
end
|
118
|
+
|
119
|
+
def cached_license
|
120
|
+
@cached_license ||= gen_license
|
121
|
+
end
|
122
|
+
|
123
|
+
def gen_license
|
124
|
+
template = if license.is_a?( Symbol )
|
125
|
+
IO.read( File.join( TDIR, 'license', license.to_s ) )
|
126
|
+
else
|
127
|
+
license.to_s
|
128
|
+
end
|
129
|
+
expand( template, binding )
|
130
|
+
end
|
131
|
+
|
132
|
+
def expand( template, bnd )
|
133
|
+
ERB.new( template, nil, '%' ).result( bnd ).map { |l| l.rstrip }
|
134
|
+
end
|
135
|
+
|
136
|
+
def years
|
137
|
+
@years ||= [ inception, Time.now.year ].uniq.join( '-' )
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class HeaderProcessor
|
142
|
+
|
143
|
+
CMAP = {}
|
144
|
+
|
145
|
+
def self.mk_tag( name, color )
|
146
|
+
tag = name.foreground( color )
|
147
|
+
CMAP[ tag ] = color
|
148
|
+
tag
|
149
|
+
end
|
150
|
+
|
151
|
+
GOOD = mk_tag( "GOOD ", :green )
|
152
|
+
NONE = mk_tag( "NONE ", :red )
|
153
|
+
DATE = mk_tag( "DATE ", :cyan )
|
154
|
+
EMPTY = mk_tag( "EMPTY", :yellow )
|
155
|
+
WROTE = mk_tag( "WROTE", :yellow )
|
156
|
+
|
157
|
+
def initialize( fname, do_write = false )
|
158
|
+
@cpos = 0
|
159
|
+
@do_write = do_write
|
160
|
+
@fname = fname
|
161
|
+
@format = case fname
|
162
|
+
when /\.java$/
|
163
|
+
:java
|
164
|
+
when /\.xml$/
|
165
|
+
:xml
|
166
|
+
when /\.rb$/
|
167
|
+
:rb
|
168
|
+
else
|
169
|
+
:txt
|
170
|
+
end
|
171
|
+
@state = :first
|
172
|
+
@writer = HeaderWriter.instance
|
173
|
+
end
|
174
|
+
|
175
|
+
def process
|
176
|
+
state = GOOD
|
177
|
+
@lines = IO.readlines( @fname )
|
178
|
+
if @lines.empty?
|
179
|
+
state = EMPTY
|
180
|
+
else
|
181
|
+
scan_prolog
|
182
|
+
if find_copyright
|
183
|
+
if !check_copyright
|
184
|
+
if @do_write
|
185
|
+
rewrite_file
|
186
|
+
state = WROTE
|
187
|
+
else
|
188
|
+
state = DATE
|
189
|
+
end
|
190
|
+
end
|
191
|
+
else
|
192
|
+
if @do_write
|
193
|
+
insert_header
|
194
|
+
rewrite_file
|
195
|
+
state = WROTE
|
196
|
+
else
|
197
|
+
state = NONE
|
198
|
+
end
|
199
|
+
end
|
200
|
+
end
|
201
|
+
puts( "%s %s" %
|
202
|
+
[ state, @fname.dup.foreground( CMAP[ state ] ) ] )
|
203
|
+
end
|
204
|
+
|
205
|
+
def rewrite_file
|
206
|
+
open( @fname, "w" ) { |fout| fout.puts( @lines ) }
|
207
|
+
end
|
208
|
+
|
209
|
+
def scan_prolog
|
210
|
+
|
211
|
+
if @lines[0] =~ /^#\!([^\s]+)/
|
212
|
+
@format = :rb if $1 =~ /ruby$/
|
213
|
+
@cpos = 1
|
214
|
+
end
|
215
|
+
|
216
|
+
if @lines[0] =~ /^<\?xml/
|
217
|
+
@format = :xml
|
218
|
+
@cpos = 1
|
219
|
+
end
|
220
|
+
|
221
|
+
@lines.each_index do |i|
|
222
|
+
line = @lines[i]
|
223
|
+
if line =~ /^#.*-\*-\s*ruby\s*-\*-/
|
224
|
+
@format = :rb
|
225
|
+
@cpos = i+1
|
226
|
+
break
|
227
|
+
else
|
228
|
+
break if line !~ /^\s*#/
|
229
|
+
end
|
230
|
+
end
|
231
|
+
|
232
|
+
end
|
233
|
+
|
234
|
+
def find_copyright
|
235
|
+
@cline = nil
|
236
|
+
@lines.each_index do |i|
|
237
|
+
line = @lines[i]
|
238
|
+
case @format
|
239
|
+
when :rb
|
240
|
+
case line
|
241
|
+
when /^#\s+Copyright/
|
242
|
+
@cline = i
|
243
|
+
break
|
244
|
+
when /^\s*$/
|
245
|
+
when /^\s*[^#]/
|
246
|
+
break
|
247
|
+
end
|
248
|
+
when :java
|
249
|
+
case line
|
250
|
+
when /^\s*\*\s+Copyright/
|
251
|
+
@cline = i
|
252
|
+
break
|
253
|
+
when /^\s*$/
|
254
|
+
when /^\s*[^\/\*]/
|
255
|
+
break
|
256
|
+
end
|
257
|
+
else
|
258
|
+
if line =~ /Copyright \([cC]\)/
|
259
|
+
@cline = i
|
260
|
+
break
|
261
|
+
end
|
262
|
+
end
|
263
|
+
end
|
264
|
+
@cline
|
265
|
+
end
|
266
|
+
|
267
|
+
def check_copyright
|
268
|
+
passes =
|
269
|
+
if @lines[@cline] =~ /Copyright \(c\) (\d{4})(-(\d{4}))? (\S.*)\s*$/
|
270
|
+
ldate = $3 || $1 #last date
|
271
|
+
ldate && ( ldate == Time.now.year.to_s ) && ( $4 == @writer.holder )
|
272
|
+
else
|
273
|
+
false
|
274
|
+
end
|
275
|
+
|
276
|
+
unless passes
|
277
|
+
start = @lines[@cline].match( /^.*Copyright/ )
|
278
|
+
@lines[@cline] = [ start, "(c)",
|
279
|
+
@writer.years, @writer.holder ].join( ' ' )
|
280
|
+
end
|
281
|
+
|
282
|
+
passes
|
283
|
+
end
|
284
|
+
|
285
|
+
def insert_header
|
286
|
+
header = @writer.cached_header( @format )
|
287
|
+
@lines.insert( @cpos, *header )
|
288
|
+
@cpos += header.length
|
289
|
+
# Insert an extra line break if needed.
|
290
|
+
unless @lines[ @cpos ] =~ /^\s*$/ || @format == :xml
|
291
|
+
@lines.insert( @cpos, "" )
|
292
|
+
end
|
293
|
+
end
|
294
|
+
|
295
|
+
end
|
296
|
+
|
297
|
+
end
|
298
|
+
end
|
@@ -0,0 +1,135 @@
|
|
1
|
+
#--
|
2
|
+
# Copyright (c) 2008-2010 David Kellum
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License"); you
|
5
|
+
# may not use this file except in compliance with the License. You may
|
6
|
+
# obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
13
|
+
# implied. See the License for the specific language governing
|
14
|
+
# permissions and limitations under the License.
|
15
|
+
#++
|
16
|
+
|
17
|
+
require 'gravitext-devtools'
|
18
|
+
require 'fileutils'
|
19
|
+
|
20
|
+
module Gravitext
|
21
|
+
module DevTools
|
22
|
+
|
23
|
+
class ManifestWriter
|
24
|
+
include FileUtils
|
25
|
+
include Gravitext::DevTools
|
26
|
+
|
27
|
+
# Set true if Manifest.txt should be written instead of Manifest.txt.
|
28
|
+
# (Default: true if Manifest.static exists)
|
29
|
+
# Boolean
|
30
|
+
attr_accessor :static
|
31
|
+
|
32
|
+
def initialize
|
33
|
+
@verbose = false
|
34
|
+
@static = File.exist?( 'Manifest.static' )
|
35
|
+
|
36
|
+
@git_lister = GitFileLister.new
|
37
|
+
|
38
|
+
@git_lister.exclusions = [ %r{(^|/).gitignore$},
|
39
|
+
%r{(^|/).gt-config$},
|
40
|
+
%r{(^|/)src(/|$)},
|
41
|
+
'lib/**/*.jar',
|
42
|
+
'Manifest.static' ]
|
43
|
+
end
|
44
|
+
|
45
|
+
def parse_options( args = ARGV )
|
46
|
+
|
47
|
+
@git_lister.parse_options( args ) do |opts|
|
48
|
+
opts.banner = "Usage: gt-manifest [dir|file] ..."
|
49
|
+
opts.on( "-v", "--verbose",
|
50
|
+
"Output full manifest details" ) do
|
51
|
+
@verbose = true
|
52
|
+
end
|
53
|
+
opts.on( "-s", "--static",
|
54
|
+
"Write to Manifest.static instead of Manifest.txt" ) do
|
55
|
+
@static = true
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def run( args = ARGV )
|
61
|
+
|
62
|
+
parse_options( args )
|
63
|
+
|
64
|
+
@git_lister.extra_files << 'Manifest.txt' unless @static
|
65
|
+
@git_lister.git_flags << '-c' # Include cached by default
|
66
|
+
|
67
|
+
files = @git_lister.files
|
68
|
+
|
69
|
+
files.map! do |f|
|
70
|
+
f = f.split( File::SEPARATOR )
|
71
|
+
f.shift if f[0] == '.'
|
72
|
+
f
|
73
|
+
end
|
74
|
+
files.uniq!
|
75
|
+
files = sort( files )
|
76
|
+
files.map! { |f| File.join( f ) }
|
77
|
+
|
78
|
+
open( 'Manifest.' + ( @static ? 'static' : 'txt' ), 'w' ) do |out|
|
79
|
+
files.each do |fname|
|
80
|
+
puts fname if @verbose
|
81
|
+
out.puts fname
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def sort( files )
|
87
|
+
|
88
|
+
files = files.sort do |a,b|
|
89
|
+
i = 0
|
90
|
+
o = 0
|
91
|
+
while( o == 0 )
|
92
|
+
o = -1 if ( a.length == i+1 ) && a.length < b.length
|
93
|
+
o = 1 if ( b.length == i+1 ) && a.length > b.length
|
94
|
+
o = a[i] <=> b[i] if o == 0
|
95
|
+
i += 1
|
96
|
+
end
|
97
|
+
o
|
98
|
+
end
|
99
|
+
|
100
|
+
files = priority_to_base( files )
|
101
|
+
|
102
|
+
files
|
103
|
+
end
|
104
|
+
|
105
|
+
# Move up any foo/base.rb or version.rb files. By convention
|
106
|
+
# (originally based on rdoc issues) these come before a foo.rb, i.e:
|
107
|
+
#
|
108
|
+
# lib/foo/base.rb
|
109
|
+
# lib/foo.rb
|
110
|
+
# lib/foo/other.rb
|
111
|
+
#
|
112
|
+
def priority_to_base( files )
|
113
|
+
|
114
|
+
bases, nfiles = files.partition { |f| f.last =~ /^(base|version)\.rb$/ }
|
115
|
+
|
116
|
+
bases.each do |base|
|
117
|
+
key = base[0..-2]
|
118
|
+
key[-1] += ".rb"
|
119
|
+
nfiles.each_with_index do |file, i|
|
120
|
+
if file == key
|
121
|
+
nfiles.insert( i, base )
|
122
|
+
base = nil
|
123
|
+
break
|
124
|
+
end
|
125
|
+
end
|
126
|
+
return files if base
|
127
|
+
end
|
128
|
+
|
129
|
+
nfiles
|
130
|
+
end
|
131
|
+
|
132
|
+
end
|
133
|
+
|
134
|
+
end
|
135
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
Copyright (c) <%= years %> <%= holder %>
|
2
|
+
|
3
|
+
Licensed under the Apache License, Version 2.0 (the "License"); you
|
4
|
+
may not use this file except in compliance with the License. You may
|
5
|
+
obtain a copy of the License at
|
6
|
+
|
7
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
|
9
|
+
Unless required by applicable law or agreed to in writing, software
|
10
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
12
|
+
implied. See the License for the specific language governing
|
13
|
+
permissions and limitations under the License.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Copyright (c) <%= years %> <%= holder %>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions are
|
6
|
+
met:
|
7
|
+
|
8
|
+
* Redistributions of source code must retain the above copyright
|
9
|
+
notice, this list of conditions and the following disclaimer.
|
10
|
+
* Redistributions in binary form must reproduce the above copyright
|
11
|
+
notice, this list of conditions and the following disclaimer in the
|
12
|
+
documentation and/or other materials provided with the distribution.
|
13
|
+
* Neither the name of RJack nor the names of its contributors may be
|
14
|
+
used to endorse or promote products derived from this software
|
15
|
+
without specific prior written permission.
|
16
|
+
|
17
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
18
|
+
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
19
|
+
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
20
|
+
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
21
|
+
HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
22
|
+
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
23
|
+
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
24
|
+
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
25
|
+
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
26
|
+
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
27
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) <%= years %> <%= holder %>
|
2
|
+
All rights reserved.
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
metadata
ADDED
@@ -0,0 +1,93 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: gravitext-devtools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Kellum
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-04-21 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: rjack-tarpit
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ~>
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.2.1
|
24
|
+
version:
|
25
|
+
description: |-
|
26
|
+
A collection of development support tools.
|
27
|
+
|
28
|
+
gt-manifest:: Maintain manifest from git ls-files minus exclusions.
|
29
|
+
gt-cleanws:: Cleanup whitespace in files.
|
30
|
+
gt-header:: Add and maintain file license headers.
|
31
|
+
email:
|
32
|
+
- dek-oss@gravitext.com
|
33
|
+
executables:
|
34
|
+
- gt-cleanws
|
35
|
+
- gt-header
|
36
|
+
- gt-manifest
|
37
|
+
extensions: []
|
38
|
+
|
39
|
+
extra_rdoc_files:
|
40
|
+
- Manifest.txt
|
41
|
+
- templates/format/format.txt
|
42
|
+
- README.rdoc
|
43
|
+
- History.rdoc
|
44
|
+
files:
|
45
|
+
- History.rdoc
|
46
|
+
- Manifest.txt
|
47
|
+
- README.rdoc
|
48
|
+
- Rakefile
|
49
|
+
- bin/gt-cleanws
|
50
|
+
- bin/gt-header
|
51
|
+
- bin/gt-manifest
|
52
|
+
- lib/gravitext-devtools/base.rb
|
53
|
+
- lib/gravitext-devtools.rb
|
54
|
+
- lib/gravitext-devtools/header_writer.rb
|
55
|
+
- lib/gravitext-devtools/manifest_writer.rb
|
56
|
+
- templates/format/format.java
|
57
|
+
- templates/format/format.rb
|
58
|
+
- templates/format/format.txt
|
59
|
+
- templates/format/format.xml
|
60
|
+
- templates/license/apache
|
61
|
+
- templates/license/bsd
|
62
|
+
- templates/license/mit
|
63
|
+
has_rdoc: true
|
64
|
+
homepage: http://github.com/dekellum/gravitext-devtools
|
65
|
+
licenses: []
|
66
|
+
|
67
|
+
post_install_message:
|
68
|
+
rdoc_options:
|
69
|
+
- --main
|
70
|
+
- README.rdoc
|
71
|
+
require_paths:
|
72
|
+
- lib
|
73
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - ">="
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: "0"
|
78
|
+
version:
|
79
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: "0"
|
84
|
+
version:
|
85
|
+
requirements: []
|
86
|
+
|
87
|
+
rubyforge_project: gravitext-devtools
|
88
|
+
rubygems_version: 1.3.5
|
89
|
+
signing_key:
|
90
|
+
specification_version: 3
|
91
|
+
summary: A collection of development support tools
|
92
|
+
test_files: []
|
93
|
+
|