crosscase 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/MANIFEST +6 -0
- data/README +64 -0
- data/crosscase.gemspec +21 -0
- data/crosscase.rb +297 -0
- data/install.rb +85 -0
- data/test.rb +67 -0
- data/utils.rb +400 -0
- metadata +46 -0
data/MANIFEST
ADDED
data/README
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
|
2
|
+
= CrossCase
|
3
|
+
|
4
|
+
A mixin for auto-generating under_barred aliases for camelCased methods, and
|
5
|
+
vice-versa.
|
6
|
+
|
7
|
+
See crosscase.rb for more details.
|
8
|
+
|
9
|
+
|
10
|
+
== Authors
|
11
|
+
|
12
|
+
* Michael Granger <ged@FaerieMUD.org>
|
13
|
+
|
14
|
+
=== Thanks
|
15
|
+
|
16
|
+
* The denizens of #ruby-lang on irc.freenode.net, especially dblack, oGMo, and
|
17
|
+
rubyhacker1 who all helped with name suggestions.
|
18
|
+
|
19
|
+
|
20
|
+
== Requirements
|
21
|
+
|
22
|
+
* Ruby >= 1.8.0
|
23
|
+
|
24
|
+
|
25
|
+
== Installation
|
26
|
+
|
27
|
+
$ su
|
28
|
+
# ruby install.rb
|
29
|
+
|
30
|
+
|
31
|
+
=== Documentation
|
32
|
+
|
33
|
+
If you have Dave Thomas's RDoc system installed, you can generate your very own
|
34
|
+
HTML documentation for this module like so:
|
35
|
+
|
36
|
+
$ rdoc crosscase.rb README
|
37
|
+
|
38
|
+
|
39
|
+
== More Information
|
40
|
+
|
41
|
+
You can find more information about this module, including any updates, at its
|
42
|
+
project page:
|
43
|
+
|
44
|
+
http://www.deveiate.org/code/CrossCase.shtml
|
45
|
+
|
46
|
+
|
47
|
+
== Legal
|
48
|
+
|
49
|
+
Copyright � 2003 The FaerieMUD Consortium. All rights reserved.
|
50
|
+
|
51
|
+
This module is free software. You may use, modify, and/or redistribute this
|
52
|
+
software under the same terms as Ruby.
|
53
|
+
|
54
|
+
THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
|
55
|
+
INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF MERCHANTIBILITY AND
|
56
|
+
FITNESS FOR A PARTICULAR PURPOSE.
|
57
|
+
|
58
|
+
|
59
|
+
$Id: README,v 1.1 2003/07/25 16:54:09 deveiant Exp $
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
data/crosscase.gemspec
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'date'
|
2
|
+
Gem::Specification.new do |s|
|
3
|
+
s.name = %q{crosscase}
|
4
|
+
s.version = "0.0.1"
|
5
|
+
s.date = Date.today.to_s
|
6
|
+
s.summary = %q{A mixin for auto-generating under_barred aliases for camelCased methods, and vice-versa.}
|
7
|
+
s.description =<<DESCRIPTION
|
8
|
+
A mixin for auto-generating under_barred aliases for camelCased methods, and vice-versa.
|
9
|
+
DESCRIPTION
|
10
|
+
s.author = %q{Michael Granger}
|
11
|
+
s.email = %q{ged@FaerieMUD.org}
|
12
|
+
s.homepage = %q{http://www.deveiate.org/code/CrossCase.html}
|
13
|
+
s.files = Dir.glob('**/*')
|
14
|
+
s.require_path = %q{.}
|
15
|
+
s.autorequire = %q{crosscase}
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.rdoc_options = ["--main", "README"]
|
18
|
+
s.extra_rdoc_files = ["README"]
|
19
|
+
s.test_files = %w{test.rb}
|
20
|
+
s.required_ruby_version = Gem::Version::Requirement.new(">= 1.8.0")
|
21
|
+
end
|
data/crosscase.rb
ADDED
@@ -0,0 +1,297 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# CrossCase - a mixin for making methods look the way you like 'em. Or for
|
4
|
+
# making them look the way other people like 'em. Or something.
|
5
|
+
#
|
6
|
+
# == Synopsis
|
7
|
+
#
|
8
|
+
# class MyClass
|
9
|
+
# include CrossCase
|
10
|
+
#
|
11
|
+
# def underbarred_method; ...; end
|
12
|
+
# def camelCasedMethod; ...; end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# obj = MyClass::new
|
16
|
+
# obj.underbarredMethod
|
17
|
+
# obj.camel_cased_method
|
18
|
+
#
|
19
|
+
# # -or-
|
20
|
+
#
|
21
|
+
# class MyClass
|
22
|
+
# def underbarred_method; ...; end
|
23
|
+
# def camelCasedMethod; ...; end
|
24
|
+
# end
|
25
|
+
#
|
26
|
+
# MyClass.extend( CrossCase )
|
27
|
+
# obj = MyClass::new
|
28
|
+
# obj.underbarredMethod
|
29
|
+
# obj.camel_cased_method
|
30
|
+
#
|
31
|
+
# == Description
|
32
|
+
#
|
33
|
+
# This module, when mixed into a Class or another Module, will provide
|
34
|
+
# under_barred aliases for class or instance methods with names which follow the
|
35
|
+
# camelCased naming conventions, and vice-versa. E.g., in a class which mixes in
|
36
|
+
# CrossCase, defining a method which is called +foo_bar+ will also create an
|
37
|
+
# alias for that method called +fooBar+.
|
38
|
+
#
|
39
|
+
# I wrote this module because I prefer camelCased method names, but also wish to
|
40
|
+
# respect the preferences of my fellow Rubyists for whom such practices are an
|
41
|
+
# abomination. And I'm too lazy to type
|
42
|
+
# alias :twinkle_twinkle :twinkleTwinkle
|
43
|
+
# for every method. It's all about laziness. Or perhaps I'm catering to my
|
44
|
+
# hubris. Or both; I'll shut up now.
|
45
|
+
#
|
46
|
+
# == Caveats
|
47
|
+
#
|
48
|
+
# This module uses the +method_added+ and +singleton_method_added+ hooks to
|
49
|
+
# generate aliases for new methods. If either or both of these methods are
|
50
|
+
# already defined, they will be preserved as aliases when the Class or Module is
|
51
|
+
# extended. It's up to you to return the favor should you create your own hook
|
52
|
+
# after this module is mixed in to your class.
|
53
|
+
#
|
54
|
+
# == Authors
|
55
|
+
#
|
56
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
57
|
+
#
|
58
|
+
# === Thanks To
|
59
|
+
#
|
60
|
+
# * The denizens of irc://irc.freenode.net/#ruby-lang, and especially dblack,
|
61
|
+
# oGMo, and rubyhacker1 for name suggestions.
|
62
|
+
#
|
63
|
+
# == Copyright
|
64
|
+
#
|
65
|
+
# Copyright (c) 2003 The FaerieMUD Consortium. All rights reserved.
|
66
|
+
#
|
67
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
68
|
+
# software under the same terms as Ruby
|
69
|
+
#
|
70
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
71
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
72
|
+
# FOR A PARTICULAR PURPOSE.
|
73
|
+
#
|
74
|
+
# == Version
|
75
|
+
#
|
76
|
+
# $Id: crosscase.rb,v 1.2 2003/07/25 17:00:24 deveiant Exp $
|
77
|
+
#
|
78
|
+
|
79
|
+
|
80
|
+
### A mixin which causes aliases for methods with either under_barred or
|
81
|
+
### camelCased naming conventions to be created in the opposing
|
82
|
+
### convention. E.g., in a class which mixes in CrossCase, defining a method
|
83
|
+
### which is called +foo_bar+ will also create an alias for that method called
|
84
|
+
### +fooBar+.
|
85
|
+
module CrossCase
|
86
|
+
|
87
|
+
### Versioning constants
|
88
|
+
Version = /([\d\.]+)/.match( %q{$Revision: 1.2 $} )[1]
|
89
|
+
Rcsid = %q$Id: crosscase.rb,v 1.2 2003/07/25 17:00:24 deveiant Exp $
|
90
|
+
|
91
|
+
### The inclusion callback -- uses the Ouroboros trick to extend including
|
92
|
+
### classes.
|
93
|
+
def self::included( mod )
|
94
|
+
mod.extend( self )
|
95
|
+
end
|
96
|
+
|
97
|
+
|
98
|
+
### Object-extension callback -- installs aliases for any currently-extant
|
99
|
+
### class or instance methods, and installs callbacks that will create
|
100
|
+
### aliases for any subsequently-defined methods. Raises an error if any
|
101
|
+
### object except a Class or Module is extended.
|
102
|
+
def self::extend_object( mod )
|
103
|
+
raise TypeError, "Expected a Module or a Class, got a " +
|
104
|
+
mod.class.name unless
|
105
|
+
mod.is_a?( Module )
|
106
|
+
|
107
|
+
self::transformClassMethods( mod )
|
108
|
+
self::transformInstanceMethods( mod )
|
109
|
+
self::installMethodHooks( mod )
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
### Install +method_added+ and +singleton_method_added+ hooks into the given
|
114
|
+
### Module +mod+ which auto-generate aliases for new methods.
|
115
|
+
def self::installMethodHooks( mod )
|
116
|
+
mod.module_eval {
|
117
|
+
class << self
|
118
|
+
if respond_to?( :singleton_method_added )
|
119
|
+
alias :__cc_sma :singleton_method_added
|
120
|
+
end
|
121
|
+
def singleton_method_added( id )
|
122
|
+
if aliasName = CrossCase::transform( id )
|
123
|
+
CrossCase::installClassAlias( self, id, aliasName )
|
124
|
+
end
|
125
|
+
if respond_to?( :__cc_sma )
|
126
|
+
__cc_sma( id )
|
127
|
+
else
|
128
|
+
super
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
if respond_to?( :method_added )
|
133
|
+
alias :__cc_ma :method_added
|
134
|
+
end
|
135
|
+
def method_added( id )
|
136
|
+
if aliasName = CrossCase::transform( id )
|
137
|
+
CrossCase::installAlias( self, id, aliasName )
|
138
|
+
end
|
139
|
+
if respond_to?( :__cc_ma )
|
140
|
+
__cc_ma( id )
|
141
|
+
else
|
142
|
+
super
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
### Search for and install aliases for either underbarred or camelCased
|
151
|
+
### class methods for +mod+ (a Class or Module).
|
152
|
+
def self::transformClassMethods( mod )
|
153
|
+
self::findTargetMethods( mod.singleton_methods(false) ) {|meth, aliasName|
|
154
|
+
self::installClassAlias( mod, meth, aliasName )
|
155
|
+
}
|
156
|
+
end
|
157
|
+
|
158
|
+
|
159
|
+
### Install an alias +aliasName+ for the given class method +meth+ of the
|
160
|
+
### Class or Module +mod+.
|
161
|
+
def self::installClassAlias( mod, meth, aliasName )
|
162
|
+
unless mod.respond_to?( aliasName )
|
163
|
+
code = %{
|
164
|
+
class << self; alias_method( :#{aliasName}, :#{meth} ); end
|
165
|
+
}
|
166
|
+
mod.module_eval( code, __FILE__, __LINE__ )
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
|
171
|
+
### Search for and install aliases for either underbarred or camelCased
|
172
|
+
### instance methods for +mod+ (a Class or Module).
|
173
|
+
def self::transformInstanceMethods( mod )
|
174
|
+
self::findTargetMethods( mod.instance_methods(false) ) {|meth, aliasName|
|
175
|
+
self::installAlias( mod, meth, aliasName )
|
176
|
+
}
|
177
|
+
end
|
178
|
+
|
179
|
+
|
180
|
+
### Install an alias +aliasName+ for the given instance method +meth+ of the
|
181
|
+
### Class or Module +mod+.
|
182
|
+
def self::installAlias( mod, meth, aliasName )
|
183
|
+
unless mod.instance_methods(true).include?( aliasName )
|
184
|
+
mod.module_eval %{alias_method :#{aliasName}, :#{meth}}
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
|
189
|
+
### Find methods in the given +methodList+ which are candidates for
|
190
|
+
### aliasing.
|
191
|
+
def self::findTargetMethods( *methodList )
|
192
|
+
methodList.flatten.each {|meth|
|
193
|
+
next if /(singleton_)?method_added/ =~ meth
|
194
|
+
transformedName = transform( meth ) or next
|
195
|
+
yield( meth, transformedName )
|
196
|
+
}
|
197
|
+
end
|
198
|
+
|
199
|
+
|
200
|
+
### Return an alternate name for the given method id +mid+. If the method id
|
201
|
+
### is an under_barred method, returns a camelCased version, and
|
202
|
+
### vice-versa. If no alternate is called for, returns +nil+.
|
203
|
+
def self::transform( mid )
|
204
|
+
methodName = mid.to_s
|
205
|
+
transformedName = ''
|
206
|
+
|
207
|
+
# camelCased methods
|
208
|
+
if /[A-Z]/.match( methodName ) && !/_/.match( methodName )
|
209
|
+
transformedName = methodName.gsub( /([a-z0-9])([A-Z])/ ) {|match|
|
210
|
+
$1 + '_' + $2
|
211
|
+
}.downcase
|
212
|
+
|
213
|
+
# underbarred_methods
|
214
|
+
elsif !/A-Z/.match( methodName ) && /[a-z0-9]_[a-z]/.match( methodName )
|
215
|
+
transformedName = methodName.gsub( /([a-z0-9])_([a-z])/ ) {|match|
|
216
|
+
$1 + $2.upcase
|
217
|
+
}
|
218
|
+
|
219
|
+
else
|
220
|
+
transformedName = nil
|
221
|
+
end
|
222
|
+
|
223
|
+
return transformedName
|
224
|
+
end
|
225
|
+
|
226
|
+
|
227
|
+
end
|
228
|
+
|
229
|
+
|
230
|
+
if $0 == __FILE__
|
231
|
+
require './utils'
|
232
|
+
include UtilityFunctions
|
233
|
+
$yaml = false
|
234
|
+
|
235
|
+
class Foo #:nodoc:
|
236
|
+
def self::singleton_method_added( id )
|
237
|
+
$stderr.puts "Original sma: Added #{id} to #{self.inspect}"
|
238
|
+
end
|
239
|
+
|
240
|
+
def self::method_added( id )
|
241
|
+
$stderr.puts "Original ma: Added #{id} to #{self.inspect}"
|
242
|
+
end
|
243
|
+
|
244
|
+
def self::classPreCamelMethod
|
245
|
+
"classPreCamelMethod"
|
246
|
+
end
|
247
|
+
|
248
|
+
def self::class_pre_underbarred_method
|
249
|
+
"class_pre_underbarred_method"
|
250
|
+
end
|
251
|
+
|
252
|
+
def preCamelCasedMethod
|
253
|
+
"preCamelCasedMethod"
|
254
|
+
end
|
255
|
+
|
256
|
+
def pre_underbarred_method
|
257
|
+
"pre_underbarred_method"
|
258
|
+
end
|
259
|
+
|
260
|
+
extend CrossCase
|
261
|
+
|
262
|
+
def self::classCamelMethod
|
263
|
+
"classCamelMethod"
|
264
|
+
end
|
265
|
+
|
266
|
+
def self::class_underbarred_method
|
267
|
+
"class_underbarred_method"
|
268
|
+
end
|
269
|
+
|
270
|
+
def camelCasedMethod
|
271
|
+
"camelCasedMethod"
|
272
|
+
end
|
273
|
+
|
274
|
+
def underbarred_method
|
275
|
+
"underbarred_method"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
f = nil
|
280
|
+
try( "to instantiate Foo" ) { f = Foo::new }
|
281
|
+
%w{classPreCamelMethod class_pre_camel_method
|
282
|
+
class_pre_underbarred_method classPreUnderbarredMethod
|
283
|
+
classCamelMethod class_camel_method
|
284
|
+
class_underbarred_method classUnderbarredMethod
|
285
|
+
}.
|
286
|
+
sort.each {|meth|
|
287
|
+
try( "to call #{meth} on Foo" ) {
|
288
|
+
Foo.send( meth )
|
289
|
+
}
|
290
|
+
}
|
291
|
+
Foo.instance_methods(false).sort.each {|meth|
|
292
|
+
try( "to call #{meth} on the instance of Foo" ) {
|
293
|
+
f.send( meth )
|
294
|
+
}
|
295
|
+
}
|
296
|
+
end
|
297
|
+
|
data/install.rb
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
# install.rb
|
2
|
+
#
|
3
|
+
# $Date: 2003/07/25 15:43:29 $
|
4
|
+
# Copyright (c) 2000 Masatoshi SEKI
|
5
|
+
#
|
6
|
+
# install.rb is copyrighted free software by Masatoshi SEKI.
|
7
|
+
# You can redistribute it and/or modify it under the same term as Ruby.
|
8
|
+
|
9
|
+
require 'rbconfig'
|
10
|
+
require 'find'
|
11
|
+
require 'ftools'
|
12
|
+
|
13
|
+
include Config
|
14
|
+
|
15
|
+
class Installer
|
16
|
+
protected
|
17
|
+
def install(from, to, mode = nil, verbose = false)
|
18
|
+
str = "install '#{from}' to '#{to}'"
|
19
|
+
str += ", mode=#{mode}" if mode
|
20
|
+
puts str if verbose
|
21
|
+
end
|
22
|
+
|
23
|
+
protected
|
24
|
+
def makedirs(*dirs)
|
25
|
+
for d in dirs
|
26
|
+
puts "mkdir #{d}"
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(test=false)
|
31
|
+
@version = CONFIG["MAJOR"]+"."+CONFIG["MINOR"]
|
32
|
+
@libdir = File.join(CONFIG["libdir"], "ruby", @version)
|
33
|
+
@sitelib = find_site_libdir
|
34
|
+
@ftools = (test) ? self : File
|
35
|
+
end
|
36
|
+
public
|
37
|
+
attr_reader(:libdir, :sitelib)
|
38
|
+
|
39
|
+
private
|
40
|
+
def find_site_libdir
|
41
|
+
site_libdir = $:.find {|x| x =~ /site_ruby$/}
|
42
|
+
if !site_libdir
|
43
|
+
site_libdir = File.join(@libdir, "site_ruby")
|
44
|
+
elsif site_libdir !~ Regexp::new( Regexp.quote(@version) )
|
45
|
+
site_libdir = File.join(site_libdir, @version)
|
46
|
+
end
|
47
|
+
site_libdir
|
48
|
+
end
|
49
|
+
|
50
|
+
public
|
51
|
+
def files_in_dir(dir)
|
52
|
+
list = []
|
53
|
+
Find.find(dir) do |f|
|
54
|
+
list.push(f)
|
55
|
+
end
|
56
|
+
list
|
57
|
+
end
|
58
|
+
|
59
|
+
public
|
60
|
+
def install_files(srcdir, files, destdir=@sitelib)
|
61
|
+
path = []
|
62
|
+
dir = []
|
63
|
+
|
64
|
+
for f in files
|
65
|
+
next if (f = f[srcdir.length+1..-1]) == nil
|
66
|
+
path.push f if File.ftype(File.join(srcdir, f)) == 'file'
|
67
|
+
dir |= [ File.dirname(File.join(destdir, f)) ]
|
68
|
+
end
|
69
|
+
@ftools.makedirs(*dir)
|
70
|
+
for f in path
|
71
|
+
@ftools.install(File.join(srcdir, f), File.join(destdir, f), nil, true)
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
public
|
76
|
+
def install_rb
|
77
|
+
intall_files('lib', files_in_dir('lib'))
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
if __FILE__ == $0
|
82
|
+
inst = Installer.new(ARGV.shift == '-n')
|
83
|
+
inst.install_files('.', ['./crosscase.rb'])
|
84
|
+
end
|
85
|
+
|
data/test.rb
ADDED
@@ -0,0 +1,67 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
# = test.rb
|
3
|
+
#
|
4
|
+
# Test suite for CrossCase
|
5
|
+
#
|
6
|
+
# == Author
|
7
|
+
#
|
8
|
+
# Michael Granger <ged@FaerieMUD.org>
|
9
|
+
#
|
10
|
+
# Copyright (c) 2003 The FaerieMUD Consortium. All rights reserved.
|
11
|
+
#
|
12
|
+
# This program is free software. You may use, modify, and/or redistribute this
|
13
|
+
# software under the same terms as Ruby itself.
|
14
|
+
#
|
15
|
+
# This program is distributed in the hope that it will be useful, but WITHOUT
|
16
|
+
# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
17
|
+
# FOR A PARTICULAR PURPOSE.
|
18
|
+
#
|
19
|
+
# == Version
|
20
|
+
#
|
21
|
+
# $Id: test.rb,v 1.1.1.1 2003/07/25 15:43:29 deveiant Exp $
|
22
|
+
#
|
23
|
+
#
|
24
|
+
|
25
|
+
$:.unshift "."
|
26
|
+
|
27
|
+
require 'test/unit'
|
28
|
+
require 'crosscase'
|
29
|
+
|
30
|
+
$stderr.sync = $stdout.sync = true
|
31
|
+
|
32
|
+
### Reactor test case
|
33
|
+
class CrossCaseTestCase < Test::Unit::TestCase
|
34
|
+
|
35
|
+
# Test to be sure the module loaded
|
36
|
+
def test_00_module
|
37
|
+
assert_instance_of Module, CrossCase, "CrossCase module didn't load"
|
38
|
+
end
|
39
|
+
|
40
|
+
# Test pre-include
|
41
|
+
def test_10_preinclude
|
42
|
+
rval = obj = testClass = nil
|
43
|
+
|
44
|
+
assert_nothing_raised {
|
45
|
+
testClass = Class::new( Object ) {
|
46
|
+
include CrossCase
|
47
|
+
def underbarred_method; "under_barred"; end
|
48
|
+
def camelCasedMethod; "camelCased"; end
|
49
|
+
}
|
50
|
+
}
|
51
|
+
|
52
|
+
obj = testClass::new
|
53
|
+
assert_respond_to obj, :underbarredMethod,
|
54
|
+
"alias for underbarred_method"
|
55
|
+
assert_respond_to obj, :camel_cased_method,
|
56
|
+
"alias for camelCasedMethod"
|
57
|
+
end
|
58
|
+
|
59
|
+
end # class CrossCaseTestCase
|
60
|
+
|
61
|
+
|
62
|
+
|
63
|
+
|
64
|
+
|
65
|
+
|
66
|
+
|
67
|
+
|
data/utils.rb
ADDED
@@ -0,0 +1,400 @@
|
|
1
|
+
#
|
2
|
+
# Install/distribution utility functions
|
3
|
+
# $Id: utils.rb,v 1.1.1.1 2003/07/25 15:43:29 deveiant Exp $
|
4
|
+
#
|
5
|
+
# Copyright (c) 2001-2003, The FaerieMUD Consortium.
|
6
|
+
#
|
7
|
+
# This is free software. You may use, modify, and/or redistribute this
|
8
|
+
# software under the terms of the Perl Artistic License. (See
|
9
|
+
# http://language.perl.com/misc/Artistic.html)
|
10
|
+
#
|
11
|
+
|
12
|
+
|
13
|
+
BEGIN {
|
14
|
+
begin
|
15
|
+
require 'readline'
|
16
|
+
include Readline
|
17
|
+
rescue LoadError => e
|
18
|
+
$stderr.puts "Faking readline..."
|
19
|
+
def readline( prompt )
|
20
|
+
$stderr.print prompt.chomp
|
21
|
+
return $stdin.gets.chomp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
begin
|
26
|
+
require 'yaml'
|
27
|
+
$yaml = true
|
28
|
+
rescue LoadError => e
|
29
|
+
$stderr.puts "No YAML; try() will use .inspect instead."
|
30
|
+
$yaml = false
|
31
|
+
end
|
32
|
+
}
|
33
|
+
|
34
|
+
module UtilityFunctions
|
35
|
+
|
36
|
+
# The list of regexen that eliminate files from the MANIFEST
|
37
|
+
ANTIMANIFEST = [
|
38
|
+
/makedist\.rb/,
|
39
|
+
/\bCVS\b/,
|
40
|
+
/~$/,
|
41
|
+
/^#/,
|
42
|
+
%r{docs/html},
|
43
|
+
%r{docs/man},
|
44
|
+
/^TEMPLATE/,
|
45
|
+
/\.cvsignore/,
|
46
|
+
/\.s?o$/
|
47
|
+
]
|
48
|
+
|
49
|
+
# Set some ANSI escape code constants (Shamelessly stolen from Perl's
|
50
|
+
# Term::ANSIColor by Russ Allbery <rra@stanford.edu> and Zenin <zenin@best.com>
|
51
|
+
AnsiAttributes = {
|
52
|
+
'clear' => 0,
|
53
|
+
'reset' => 0,
|
54
|
+
'bold' => 1,
|
55
|
+
'dark' => 2,
|
56
|
+
'underline' => 4,
|
57
|
+
'underscore' => 4,
|
58
|
+
'blink' => 5,
|
59
|
+
'reverse' => 7,
|
60
|
+
'concealed' => 8,
|
61
|
+
|
62
|
+
'black' => 30, 'on_black' => 40,
|
63
|
+
'red' => 31, 'on_red' => 41,
|
64
|
+
'green' => 32, 'on_green' => 42,
|
65
|
+
'yellow' => 33, 'on_yellow' => 43,
|
66
|
+
'blue' => 34, 'on_blue' => 44,
|
67
|
+
'magenta' => 35, 'on_magenta' => 45,
|
68
|
+
'cyan' => 36, 'on_cyan' => 46,
|
69
|
+
'white' => 37, 'on_white' => 47
|
70
|
+
}
|
71
|
+
|
72
|
+
ErasePreviousLine = "\033[A\033[K"
|
73
|
+
|
74
|
+
|
75
|
+
###############
|
76
|
+
module_function
|
77
|
+
###############
|
78
|
+
|
79
|
+
# Create a string that contains the ANSI codes specified and return it
|
80
|
+
def ansiCode( *attributes )
|
81
|
+
return '' unless /(?:vt10[03]|xterm(?:-color)?|linux)/i =~ ENV['TERM']
|
82
|
+
attr = attributes.collect {|a| AnsiAttributes[a] ? AnsiAttributes[a] : nil}.compact.join(';')
|
83
|
+
if attr.empty?
|
84
|
+
return ''
|
85
|
+
else
|
86
|
+
return "\e[%sm" % attr
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
# Test for the presence of the specified <tt>library</tt>, and output a
|
91
|
+
# message describing the test using <tt>nicename</tt>. If <tt>nicename</tt>
|
92
|
+
# is <tt>nil</tt>, the value in <tt>library</tt> is used to build a default.
|
93
|
+
def testForLibrary( library, nicename=nil )
|
94
|
+
nicename ||= library
|
95
|
+
message( "Testing for the #{nicename} library..." )
|
96
|
+
if $:.detect {|dir| File.exists?(File.join(dir,"#{library}.rb")) || File.exists?(File.join(dir,"#{library}.so"))}
|
97
|
+
message( "found.\n" )
|
98
|
+
return true
|
99
|
+
else
|
100
|
+
message( "not found.\n" )
|
101
|
+
return false
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# Test for the presence of the specified <tt>library</tt>, and output a
|
106
|
+
# message describing the problem using <tt>nicename</tt>. If
|
107
|
+
# <tt>nicename</tt> is <tt>nil</tt>, the value in <tt>library</tt> is used
|
108
|
+
# to build a default. If <tt>raaUrl</tt> and/or <tt>downloadUrl</tt> are
|
109
|
+
# specified, they are also use to build a message describing how to find the
|
110
|
+
# required library. If <tt>fatal</tt> is <tt>true</tt>, a missing library
|
111
|
+
# will cause the program to abort.
|
112
|
+
def testForRequiredLibrary( library, nicename=nil, raaUrl=nil, downloadUrl=nil, fatal=true )
|
113
|
+
nicename ||= library
|
114
|
+
unless testForLibrary( library, nicename )
|
115
|
+
msgs = [ "You are missing the required #{nicename} library.\n" ]
|
116
|
+
msgs << "RAA: #{raaUrl}\n" if raaUrl
|
117
|
+
msgs << "Download: #{downloadUrl}\n" if downloadUrl
|
118
|
+
if fatal
|
119
|
+
abort msgs.join('')
|
120
|
+
else
|
121
|
+
errorMessage msgs.join('')
|
122
|
+
end
|
123
|
+
end
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
|
127
|
+
### Output <tt>msg</tt> as a ANSI-colored program/section header (white on
|
128
|
+
### blue).
|
129
|
+
def header( msg )
|
130
|
+
msg.chomp!
|
131
|
+
$stderr.puts ansiCode( 'bold', 'white', 'on_blue' ) + msg + ansiCode( 'reset' )
|
132
|
+
$stderr.flush
|
133
|
+
end
|
134
|
+
|
135
|
+
### Output <tt>msg</tt> to STDERR and flush it.
|
136
|
+
def message( msg )
|
137
|
+
$stderr.print ansiCode( 'cyan' ) + msg + ansiCode( 'reset' )
|
138
|
+
$stderr.flush
|
139
|
+
end
|
140
|
+
|
141
|
+
### Output the specified <tt>msg</tt> as an ANSI-colored error message
|
142
|
+
### (white on red).
|
143
|
+
def errorMessage( msg )
|
144
|
+
message ansiCode( 'bold', 'white', 'on_red' ) + msg + ansiCode( 'reset' )
|
145
|
+
end
|
146
|
+
|
147
|
+
### Output the specified <tt>msg</tt> as an ANSI-colored debugging message
|
148
|
+
### (yellow on blue).
|
149
|
+
def debugMsg( msg )
|
150
|
+
return unless $DEBUG
|
151
|
+
msg.chomp!
|
152
|
+
$stderr.puts ansiCode( 'bold', 'yellow', 'on_blue' ) + ">>> #{msg}" + ansiCode( 'reset' )
|
153
|
+
$stderr.flush
|
154
|
+
end
|
155
|
+
|
156
|
+
### Erase the previous line (if supported by your terminal) and output the
|
157
|
+
### specified <tt>msg</tt> instead.
|
158
|
+
def replaceMessage( msg )
|
159
|
+
print ErasePreviousLine
|
160
|
+
message( msg )
|
161
|
+
end
|
162
|
+
|
163
|
+
### Output a divider made up of <tt>length</tt> hyphen characters.
|
164
|
+
def divider( length=75 )
|
165
|
+
puts "\r" + ("-" * length )
|
166
|
+
end
|
167
|
+
alias :writeLine :divider
|
168
|
+
|
169
|
+
### Output the specified <tt>msg</tt> colored in ANSI red and exit with a
|
170
|
+
### status of 1.
|
171
|
+
def abort( msg )
|
172
|
+
print ansiCode( 'bold', 'red' ) + "Aborted: " + msg.chomp + ansiCode( 'reset' ) + "\n\n"
|
173
|
+
Kernel.exit!( 1 )
|
174
|
+
end
|
175
|
+
|
176
|
+
### Output the specified <tt>promptString</tt> as a prompt (in green) and
|
177
|
+
### return the user's input with leading and trailing spaces removed.
|
178
|
+
def prompt( promptString )
|
179
|
+
promptString.chomp!
|
180
|
+
return readline( ansiCode('bold', 'green') + "#{promptString}: " + ansiCode('reset') ).strip
|
181
|
+
end
|
182
|
+
|
183
|
+
### Prompt the user with the given <tt>promptString</tt> via #prompt,
|
184
|
+
### substituting the given <tt>default</tt> if the user doesn't input
|
185
|
+
### anything.
|
186
|
+
def promptWithDefault( promptString, default )
|
187
|
+
response = prompt( "%s [%s]" % [ promptString, default ] )
|
188
|
+
if response.empty?
|
189
|
+
return default
|
190
|
+
else
|
191
|
+
return response
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
### Search for the program specified by the given <tt>progname</tt> in the
|
196
|
+
### user's <tt>PATH</tt>, and return the full path to it, or <tt>nil</tt> if
|
197
|
+
### no such program is in the path.
|
198
|
+
def findProgram( progname )
|
199
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each {|d|
|
200
|
+
file = File.join( d, progname )
|
201
|
+
return file if File.executable?( file )
|
202
|
+
}
|
203
|
+
return nil
|
204
|
+
end
|
205
|
+
|
206
|
+
### Using the CVS log for the given <tt>file</tt> attempt to guess what the
|
207
|
+
### next release version might be. This only works if releases are tagged
|
208
|
+
### with tags like 'RELEASE_x_y'.
|
209
|
+
def extractNextVersionFromTags( file )
|
210
|
+
message "Attempting to extract next release version from CVS tags for #{file}...\n"
|
211
|
+
raise RuntimeError, "No such file '#{file}'" unless File.exists?( file )
|
212
|
+
cvsPath = findProgram( 'cvs' ) or
|
213
|
+
raise RuntimeError, "Cannot find the 'cvs' program. Aborting."
|
214
|
+
|
215
|
+
output = %x{#{cvsPath} log #{file}}
|
216
|
+
release = [ 0, 0 ]
|
217
|
+
output.scan( /RELEASE_(\d+)_(\d+)/ ) {|match|
|
218
|
+
if $1.to_i > release[0] || $2.to_i > release[1]
|
219
|
+
release = [ $1.to_i, $2.to_i ]
|
220
|
+
replaceMessage( "Found %d.%02d...\n" % release )
|
221
|
+
end
|
222
|
+
}
|
223
|
+
|
224
|
+
if release[1] >= 99
|
225
|
+
release[0] += 1
|
226
|
+
release[1] = 1
|
227
|
+
else
|
228
|
+
release[1] += 1
|
229
|
+
end
|
230
|
+
|
231
|
+
return "%d.%02d" % release
|
232
|
+
end
|
233
|
+
|
234
|
+
### Extract the project name (CVS Repository name) for the given directory.
|
235
|
+
def extractProjectName
|
236
|
+
File.open( "CVS/Repository", "r").readline.chomp
|
237
|
+
end
|
238
|
+
|
239
|
+
### Read the specified <tt>manifestFile</tt>, which is a text file
|
240
|
+
### describing which files to package up for a distribution. The manifest
|
241
|
+
### should consist of one or more lines, each containing one filename or
|
242
|
+
### shell glob pattern.
|
243
|
+
def readManifest( manifestFile="MANIFEST" )
|
244
|
+
message "Building manifest..."
|
245
|
+
raise "Missing #{manifestFile}, please remake it" unless File.exists? manifestFile
|
246
|
+
|
247
|
+
manifest = IO::readlines( manifestFile ).collect {|line|
|
248
|
+
line.chomp
|
249
|
+
}.select {|line|
|
250
|
+
line !~ /^(\s*(#.*)?)?$/
|
251
|
+
}
|
252
|
+
|
253
|
+
filelist = []
|
254
|
+
for pat in manifest
|
255
|
+
$stderr.puts "Adding files that match '#{pat}' to the file list" if $VERBOSE
|
256
|
+
filelist |= Dir.glob( pat ).find_all {|f| FileTest.file?(f)}
|
257
|
+
end
|
258
|
+
|
259
|
+
message "found #{filelist.length} files.\n"
|
260
|
+
return filelist
|
261
|
+
end
|
262
|
+
|
263
|
+
### Given a <tt>filelist</tt> like that returned by #readManifest, remove
|
264
|
+
### the entries therein which match the Regexp objects in the given
|
265
|
+
### <tt>antimanifest</tt> and return the resultant Array.
|
266
|
+
def vetManifest( filelist, antimanifest=ANITMANIFEST )
|
267
|
+
origLength = filelist.length
|
268
|
+
message "Vetting manifest..."
|
269
|
+
|
270
|
+
for regex in antimanifest
|
271
|
+
if $VERBOSE
|
272
|
+
message "\n\tPattern /#{regex.source}/ removed: " +
|
273
|
+
filelist.find_all {|file| regex.match(file)}.join(', ')
|
274
|
+
end
|
275
|
+
filelist.delete_if {|file| regex.match(file)}
|
276
|
+
end
|
277
|
+
|
278
|
+
message "removed #{origLength - filelist.length} files from the list.\n"
|
279
|
+
return filelist
|
280
|
+
end
|
281
|
+
|
282
|
+
### Combine a call to #readManifest with one to #vetManifest.
|
283
|
+
def getVettedManifest( manifestFile="MANIFEST", antimanifest=ANTIMANIFEST )
|
284
|
+
vetManifest( readManifest(manifestFile), antimanifest )
|
285
|
+
end
|
286
|
+
|
287
|
+
### Given a documentation <tt>catalogFile</tt>, which is in the same format
|
288
|
+
### as that described by #readManifest, read and expand it, and then return
|
289
|
+
### a list of those files which appear to have RDoc documentation in
|
290
|
+
### them. If <tt>catalogFile</tt> is nil or does not exist, the MANIFEST
|
291
|
+
### file is used instead.
|
292
|
+
def findRdocableFiles( catalogFile="docs/CATALOG" )
|
293
|
+
startlist = []
|
294
|
+
if File.exists? catalogFile
|
295
|
+
message "Using CATALOG file (%s).\n" % catalogFile
|
296
|
+
startlist = getVettedManifest( catalogFile )
|
297
|
+
else
|
298
|
+
message "Using default MANIFEST\n"
|
299
|
+
startlist = getVettedManifest()
|
300
|
+
end
|
301
|
+
|
302
|
+
message "Looking for RDoc comments in:\n" if $VERBOSE
|
303
|
+
startlist.select {|fn|
|
304
|
+
message " #{fn}: " if $VERBOSE
|
305
|
+
found = false
|
306
|
+
File::open( fn, "r" ) {|fh|
|
307
|
+
fh.each {|line|
|
308
|
+
if line =~ /^(\s*#)?\s*=/ || line =~ /:\w+:/ || line =~ %r{/\*}
|
309
|
+
found = true
|
310
|
+
break
|
311
|
+
end
|
312
|
+
}
|
313
|
+
}
|
314
|
+
|
315
|
+
message( (found ? "yes" : "no") + "\n" ) if $VERBOSE
|
316
|
+
found
|
317
|
+
}
|
318
|
+
end
|
319
|
+
|
320
|
+
### Open a file and filter each of its lines through the given block a
|
321
|
+
### <tt>line</tt> at a time. The return value of the block is used as the
|
322
|
+
### new line, or omitted if the block returns <tt>nil</tt> or
|
323
|
+
### <tt>false</tt>.
|
324
|
+
def editInPlace( file ) # :yields: line
|
325
|
+
raise "No block specified for editing operation" unless block_given?
|
326
|
+
|
327
|
+
tempName = "#{file}.#{$$}"
|
328
|
+
File::open( tempName, File::RDWR|File::CREAT, 0600 ) {|tempfile|
|
329
|
+
File::unlink( tempName )
|
330
|
+
File::open( file, File::RDONLY ) {|fh|
|
331
|
+
fh.each {|line|
|
332
|
+
newline = yield( line ) or next
|
333
|
+
tempfile.print( newline )
|
334
|
+
}
|
335
|
+
}
|
336
|
+
|
337
|
+
tempfile.seek(0)
|
338
|
+
|
339
|
+
File::open( file, File::TRUNC|File::WRONLY, 0644 ) {|newfile|
|
340
|
+
newfile.print( tempfile.read )
|
341
|
+
}
|
342
|
+
}
|
343
|
+
end
|
344
|
+
|
345
|
+
### Execute the specified shell <tt>command</tt>, read the results, and
|
346
|
+
### return them. Like a %x{} that returns an Array instead of a String.
|
347
|
+
def shellCommand( *command )
|
348
|
+
raise "Empty command" if command.empty?
|
349
|
+
|
350
|
+
cmdpipe = IO::popen( command.join(' '), 'r' )
|
351
|
+
return cmdpipe.readlines
|
352
|
+
end
|
353
|
+
|
354
|
+
### Execute a block with $VERBOSE set to +false+, restoring it to its
|
355
|
+
### previous value before returning.
|
356
|
+
def verboseOff
|
357
|
+
raise LocalJumpError, "No block given" unless block_given?
|
358
|
+
|
359
|
+
thrcrit = Thread.critical
|
360
|
+
oldverbose = $VERBOSE
|
361
|
+
begin
|
362
|
+
Thread.critical = true
|
363
|
+
$VERBOSE = false
|
364
|
+
yield
|
365
|
+
ensure
|
366
|
+
$VERBOSE = oldverbose
|
367
|
+
Thread.critical = false
|
368
|
+
end
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
### Try the specified code block, printing the given
|
373
|
+
def try( msg, bind=nil )
|
374
|
+
result = nil
|
375
|
+
message "Trying #{msg}..."
|
376
|
+
|
377
|
+
begin
|
378
|
+
rval = nil
|
379
|
+
if block_given?
|
380
|
+
rval = yield
|
381
|
+
else
|
382
|
+
file, line = caller(1)[0].split(/:/,2)
|
383
|
+
rval = eval( msg, bind, file, line.to_i )
|
384
|
+
end
|
385
|
+
|
386
|
+
if $yaml
|
387
|
+
result = rval.to_yaml
|
388
|
+
else
|
389
|
+
result = rval.inspect
|
390
|
+
end
|
391
|
+
rescue Exception => err
|
392
|
+
nicetrace = err.backtrace.delete_if {|frame|
|
393
|
+
/in `(try|eval)'/ =~ frame
|
394
|
+
}.join("\n\t")
|
395
|
+
result = err.message + "\n\t" + nicetrace
|
396
|
+
ensure
|
397
|
+
puts result
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
metadata
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: "0.8"
|
3
|
+
specification_version: 1
|
4
|
+
name: crosscase
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.0.1
|
7
|
+
date: 2004-10-27
|
8
|
+
summary: "A mixin for auto-generating under_barred aliases for camelCased methods, and vice-versa."
|
9
|
+
require_paths:
|
10
|
+
- "."
|
11
|
+
author: Michael Granger
|
12
|
+
email: ged@FaerieMUD.org
|
13
|
+
homepage: http://www.deveiate.org/code/CrossCase.html
|
14
|
+
rubyforge_project:
|
15
|
+
description: "A mixin for auto-generating under_barred aliases for camelCased methods, and vice-versa."
|
16
|
+
autorequire: crosscase
|
17
|
+
default_executable:
|
18
|
+
bindir: bin
|
19
|
+
has_rdoc: true
|
20
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
21
|
+
requirements:
|
22
|
+
-
|
23
|
+
- ">="
|
24
|
+
- !ruby/object:Gem::Version
|
25
|
+
version: 1.8.0
|
26
|
+
version:
|
27
|
+
platform: ruby
|
28
|
+
files:
|
29
|
+
- README
|
30
|
+
- crosscase.rb
|
31
|
+
- install.rb
|
32
|
+
- test.rb
|
33
|
+
- MANIFEST
|
34
|
+
- utils.rb
|
35
|
+
- crosscase.gemspec
|
36
|
+
test_files:
|
37
|
+
- test.rb
|
38
|
+
rdoc_options:
|
39
|
+
- "--main"
|
40
|
+
- README
|
41
|
+
extra_rdoc_files:
|
42
|
+
- README
|
43
|
+
executables: []
|
44
|
+
extensions: []
|
45
|
+
requirements: []
|
46
|
+
dependencies: []
|