Linguistics 1.0.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Artistic +127 -0
- data/ChangeLog +444 -0
- data/MANIFEST +19 -0
- data/README +178 -0
- data/README.english +245 -0
- data/TODO +17 -0
- data/experiments/randobjlist.rb +34 -0
- data/install.rb +154 -0
- data/lib/linguistics/en/infinitive.rb +1149 -0
- data/lib/linguistics/en/linkparser.rb +142 -0
- data/lib/linguistics/en/wordnet.rb +253 -0
- data/lib/linguistics/en.rb +1694 -0
- data/lib/linguistics/iso639.rb +456 -0
- data/lib/linguistics.rb +368 -0
- data/redist/crosscase.rb +298 -0
- data/test.rb +110 -0
- data/tests/en/conjunction.tests.rb +114 -0
- data/tests/en/inflect.tests.rb +1378 -0
- data/tests/lingtestcase.rb +239 -0
- data/tests/use.tests.rb +99 -0
- data/utils.rb +689 -0
- metadata +58 -0
data/lib/linguistics.rb
ADDED
@@ -0,0 +1,368 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
#
|
3
|
+
# linguistics.rb -- provides an interface for extending core Ruby classes with
|
4
|
+
# linguistic methods.
|
5
|
+
#
|
6
|
+
# == Synopsis
|
7
|
+
#
|
8
|
+
# require 'linguistics'
|
9
|
+
# Linguistics::use( :en )
|
10
|
+
# MyClass::extend( Linguistics )
|
11
|
+
#
|
12
|
+
# == Authors
|
13
|
+
#
|
14
|
+
# * Michael Granger <ged@FaerieMUD.org>
|
15
|
+
#
|
16
|
+
# == Copyright
|
17
|
+
#
|
18
|
+
# Copyright (c) 2003-2005 The FaerieMUD Consortium. All rights reserved.
|
19
|
+
#
|
20
|
+
# This module is free software. You may use, modify, and/or redistribute this
|
21
|
+
# software under the terms of the Perl Artistic License. (See
|
22
|
+
# http://language.perl.com/misc/Artistic.html)
|
23
|
+
#
|
24
|
+
# == Version
|
25
|
+
#
|
26
|
+
# $Id: linguistics.rb 78 2005-07-13 19:58:43Z ged $
|
27
|
+
#
|
28
|
+
|
29
|
+
require 'linguistics/iso639'
|
30
|
+
|
31
|
+
### A language-independent framework for adding linguistics functions to Ruby
|
32
|
+
### classes.
|
33
|
+
module Linguistics
|
34
|
+
|
35
|
+
### Class constants
|
36
|
+
|
37
|
+
# Subversion revision
|
38
|
+
SVNRev = %q$Rev: 78 $
|
39
|
+
|
40
|
+
# Subversion ID
|
41
|
+
SVNid = %q$Id: linguistics.rb 78 2005-07-13 19:58:43Z ged $
|
42
|
+
|
43
|
+
# Language module implementors should do something like:
|
44
|
+
# Linguistics::DefaultLanguages.push( :ja ) # or whatever
|
45
|
+
# so that direct requiring of a language module sets the default.
|
46
|
+
DefaultLanguages = []
|
47
|
+
|
48
|
+
# The list of Classes to add linguistic behaviours to.
|
49
|
+
DefaultExtClasses = [String, Numeric, Array]
|
50
|
+
|
51
|
+
|
52
|
+
#################################################################
|
53
|
+
### I N F L E C T O R C L A S S F A C T O R Y
|
54
|
+
#################################################################
|
55
|
+
|
56
|
+
### A class which is inherited from by proxies for classes being extended
|
57
|
+
### with one or more linguistic interfaces. It provides on-the-fly creation
|
58
|
+
### of linguistic methods when the <tt>:installProxy</tt> option is passed
|
59
|
+
### to the call to Linguistics#use.
|
60
|
+
class LanguageProxyClass
|
61
|
+
|
62
|
+
### Class instance variable + accessor. Contains the module which knows
|
63
|
+
### the specifics of the language the languageProxy class is providing
|
64
|
+
### methods for.
|
65
|
+
@langmod = nil
|
66
|
+
class << self
|
67
|
+
attr_accessor :langmod
|
68
|
+
end
|
69
|
+
|
70
|
+
|
71
|
+
### Create a new LanguageProxy for the given +receiver+.
|
72
|
+
def initialize( receiver )
|
73
|
+
@receiver = receiver
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
######
|
78
|
+
public
|
79
|
+
######
|
80
|
+
|
81
|
+
### Overloaded to take into account the proxy method.
|
82
|
+
def respond_to?( sym )
|
83
|
+
self.class.langmod.respond_to?( sym ) || super
|
84
|
+
end
|
85
|
+
|
86
|
+
|
87
|
+
### Autoload linguistic methods defined in the module this object's
|
88
|
+
### class uses for inflection.
|
89
|
+
def method_missing( sym, *args )
|
90
|
+
return super unless self.class.langmod.respond_to?( sym )
|
91
|
+
|
92
|
+
self.class.module_eval %{
|
93
|
+
def #{sym}( *args, &block )
|
94
|
+
self.class.langmod.#{sym}( @receiver, *args, &block )
|
95
|
+
end
|
96
|
+
}, "{Autoloaded: " + __FILE__ + "}", __LINE__
|
97
|
+
|
98
|
+
self.method( sym ).call( *args )
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
### Returns a human-readable representation of the languageProxy for
|
103
|
+
### debugging, logging, etc.
|
104
|
+
def inspect
|
105
|
+
"<%s languageProxy for %s object %s>" % [
|
106
|
+
self.class.langmod.language,
|
107
|
+
@receiver.class.name,
|
108
|
+
@receiver.inspect,
|
109
|
+
]
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
|
115
|
+
### Extend the specified target object with one or more language proxy
|
116
|
+
### methods, each of which provides access to one or more linguistic methods
|
117
|
+
### for that language.
|
118
|
+
def self::extend_object( obj )
|
119
|
+
case obj
|
120
|
+
when Class
|
121
|
+
# $stderr.puts "Extending %p" % obj if $DEBUG
|
122
|
+
self::installLanguageProxy( obj )
|
123
|
+
else
|
124
|
+
sclass = (class << obj; self; end)
|
125
|
+
# $stderr.puts "Extending a object's metaclass: %p" % obj if $DEBUG
|
126
|
+
self::installLanguageProxy( sclass )
|
127
|
+
end
|
128
|
+
|
129
|
+
super
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
### Extend the including class with linguistics proxy methods.
|
134
|
+
def self::included( mod )
|
135
|
+
# $stderr.puts "Including Linguistics in %p" % mod if $DEBUG
|
136
|
+
mod.extend( self ) unless mod == Linguistics
|
137
|
+
end
|
138
|
+
|
139
|
+
|
140
|
+
### Make an languageProxy class that encapsulates all of the inflect operations
|
141
|
+
### using the given language module.
|
142
|
+
def self::makeLanguageProxy( mod )
|
143
|
+
# $stderr.puts "Making language proxy for mod %p" % [mod]
|
144
|
+
Class::new( LanguageProxyClass ) {
|
145
|
+
@langmod = mod
|
146
|
+
}
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
### Install the language proxy
|
151
|
+
def self::installLanguageProxy( klass, languages=DefaultLanguages )
|
152
|
+
languages.replace( DefaultLanguages ) if languages.empty?
|
153
|
+
|
154
|
+
# Create an languageProxy class for each language specified
|
155
|
+
languages.each {|lang|
|
156
|
+
# $stderr.puts "Extending the %p class with %p" %
|
157
|
+
# [ klass, lang ] if $DEBUG
|
158
|
+
|
159
|
+
# Load the language module (skipping to the next if it's already
|
160
|
+
# loaded), make an languageProxy class that delegates to it, and figure
|
161
|
+
# out what the languageProxy method will be called.
|
162
|
+
mod = loadLanguage( lang.to_s.downcase )
|
163
|
+
ifaceMeth = mod.name.downcase.sub( /.*:/, '' )
|
164
|
+
languageProxyClass = makeLanguageProxy( mod )
|
165
|
+
|
166
|
+
# Install a hash for languageProxy classes and an accessor for the
|
167
|
+
# hash if it's not already present.
|
168
|
+
if !klass.class_variables.include?( "@@__languageProxy_class" )
|
169
|
+
klass.module_eval %{
|
170
|
+
@@__languageProxy_class = {}
|
171
|
+
def self::__languageProxy_class; @@__languageProxy_class; end
|
172
|
+
}, __FILE__, __LINE__
|
173
|
+
end
|
174
|
+
|
175
|
+
# Merge the current languageProxy into the hash
|
176
|
+
klass.__languageProxy_class.merge!( ifaceMeth => languageProxyClass )
|
177
|
+
|
178
|
+
# Set the language-code proxy method for the class unless it has one
|
179
|
+
# already
|
180
|
+
unless klass.instance_methods(true).include?( ifaceMeth )
|
181
|
+
klass.module_eval %{
|
182
|
+
def #{ifaceMeth}
|
183
|
+
@__#{ifaceMeth}_languageProxy ||=
|
184
|
+
self.class.__languageProxy_class["#{ifaceMeth}"].
|
185
|
+
new( self )
|
186
|
+
end
|
187
|
+
}, __FILE__, __LINE__
|
188
|
+
end
|
189
|
+
}
|
190
|
+
end
|
191
|
+
|
192
|
+
|
193
|
+
|
194
|
+
### Install a regular proxy method in the given klass that will delegate
|
195
|
+
### calls to missing method to the languageProxy for the given +language+.
|
196
|
+
def self::installDelegatorProxy( klass, langcode )
|
197
|
+
|
198
|
+
# Alias any currently-extant
|
199
|
+
if klass.instance_methods( false ).include?( "method_missing" )
|
200
|
+
klass.module_eval %{
|
201
|
+
alias_method :__orig_method_missing, :method_missing
|
202
|
+
}
|
203
|
+
end
|
204
|
+
|
205
|
+
# Add the #method_missing method that auto-installs delegator methods
|
206
|
+
# for methods supported by the linguistic proxy objects.
|
207
|
+
klass.module_eval {
|
208
|
+
define_method( :method_missing ) do |sym, *args|
|
209
|
+
|
210
|
+
if self.send( langcode ).respond_to?( sym )
|
211
|
+
|
212
|
+
# $stderr.puts "Installing linguistic delegator method #{sym} " \
|
213
|
+
# "for the '#{langcode}' proxy"
|
214
|
+
self.class.module_eval %{
|
215
|
+
def #{sym}( *args )
|
216
|
+
self.#{langcode}.#{sym}( *args )
|
217
|
+
end
|
218
|
+
}
|
219
|
+
self.method( sym ).call( *args )
|
220
|
+
|
221
|
+
# Otherwise either call the overridden proxy method if there is
|
222
|
+
# one, or just let our parent deal with it.
|
223
|
+
else
|
224
|
+
if self.respond_to?( :__orig_method_missing )
|
225
|
+
return self.__orig_method_missing( sym, *args )
|
226
|
+
else
|
227
|
+
super( sym, *args )
|
228
|
+
end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
}
|
232
|
+
end
|
233
|
+
|
234
|
+
|
235
|
+
|
236
|
+
#################################################################
|
237
|
+
### L A N G U A G E - I N D E P E N D E N T F U N C T I O N S
|
238
|
+
#################################################################
|
239
|
+
|
240
|
+
###############
|
241
|
+
module_function
|
242
|
+
###############
|
243
|
+
|
244
|
+
### Add linguistics functions for the specified languages to Ruby's core
|
245
|
+
### classes. The interface to all linguistic functions for a given language
|
246
|
+
### is through a method which is the same the language's international 2- or
|
247
|
+
### 3-letter code (ISO 639). You can also specify a Hash of configuration
|
248
|
+
### options which control which classes are extended:
|
249
|
+
###
|
250
|
+
### [<b>:classes</b>]
|
251
|
+
### Specify the classes which are to be extended. If this is not specified,
|
252
|
+
### the Class objects in Linguistics::DefaultExtClasses (an Array) are
|
253
|
+
### extended.
|
254
|
+
### [<b>:installProxy</b>]
|
255
|
+
### Install a proxy method in each of the classes which are to be extended
|
256
|
+
### which will search for missing methods in the languageProxy for the
|
257
|
+
### language code specified as the value. This allows linguistics methods
|
258
|
+
### to be called directly on extended objects directly (e.g.,
|
259
|
+
### 12.en.ordinal becomes 12.ordinal). Obviously, methods which would
|
260
|
+
### collide with the object's builtin methods will need to be invoked
|
261
|
+
### through the languageProxy. Any existing proxy methods in the extended
|
262
|
+
### classes will be preserved.
|
263
|
+
def use( *languages )
|
264
|
+
config = {}
|
265
|
+
config = languages.pop if languages.last.is_a?( Hash )
|
266
|
+
|
267
|
+
classes = config.key?( :classes ) ? config[:classes] : DefaultExtClasses
|
268
|
+
classes = [ classes ] unless classes.is_a?( Array )
|
269
|
+
|
270
|
+
# Install the languageProxy in each class.
|
271
|
+
classes.each {|klass|
|
272
|
+
|
273
|
+
# Create an languageProxy class for each installed language
|
274
|
+
installLanguageProxy( klass, languages )
|
275
|
+
|
276
|
+
# Install the delegator proxy if configured
|
277
|
+
if config[:installProxy]
|
278
|
+
case config[:installProxy]
|
279
|
+
when Symbol
|
280
|
+
langcode = config[:installProxy]
|
281
|
+
when String
|
282
|
+
langcode = config[:installProxy].intern
|
283
|
+
when TrueClass
|
284
|
+
langcode = DefaultLanguages[0]
|
285
|
+
else
|
286
|
+
raise ArgumentError,
|
287
|
+
"Unexpected value %p for :installProxy" %
|
288
|
+
config[:installProxy]
|
289
|
+
end
|
290
|
+
|
291
|
+
installDelegatorProxy( klass, langcode )
|
292
|
+
end
|
293
|
+
}
|
294
|
+
end
|
295
|
+
|
296
|
+
|
297
|
+
|
298
|
+
### Support Lingua::EN::Inflect-style globals in a threadsafe way by using
|
299
|
+
### Thread-local variables.
|
300
|
+
|
301
|
+
### Set the default count for all unspecified plurals to +val+. Setting is
|
302
|
+
### local to calling thread.
|
303
|
+
def num=( val )
|
304
|
+
Thread.current[:persistent_count] = val
|
305
|
+
end
|
306
|
+
alias_method :NUM=, :num=
|
307
|
+
|
308
|
+
### Get the default count for all unspecified plurals. Setting is local to
|
309
|
+
### calling thread.
|
310
|
+
def num
|
311
|
+
Thread.current[:persistent_count]
|
312
|
+
end
|
313
|
+
alias_method :NUM, :num
|
314
|
+
|
315
|
+
|
316
|
+
### Set the 'classical pluralizations' flag to +val+. Setting is local to
|
317
|
+
### calling thread.
|
318
|
+
def classical=( val )
|
319
|
+
Thread.current[:classical_plurals] = val
|
320
|
+
end
|
321
|
+
|
322
|
+
### Return the value of the 'classical pluralizations' flag. Setting is
|
323
|
+
### local to calling thread.
|
324
|
+
def classical?
|
325
|
+
Thread.current[:classical_plurals] ? true : false
|
326
|
+
end
|
327
|
+
|
328
|
+
|
329
|
+
#######
|
330
|
+
private
|
331
|
+
#######
|
332
|
+
|
333
|
+
### Try to load the module that implements the given language, returning
|
334
|
+
### the Module object if successful.
|
335
|
+
def self::loadLanguage( lang )
|
336
|
+
raise "Unknown language code '#{lang}'" unless
|
337
|
+
LanguageCodes.key?( lang )
|
338
|
+
|
339
|
+
# Sort all the codes for the specified language, trying the 2-letter
|
340
|
+
# versions first in alphabetical order, then the 3-letter ones
|
341
|
+
msgs = []
|
342
|
+
mod = LanguageCodes[ lang ][:codes].sort {|a,b|
|
343
|
+
(a.length <=> b.length).nonzero? ||
|
344
|
+
(a <=> b)
|
345
|
+
}.each {|code|
|
346
|
+
unless Linguistics::const_defined?( code.upcase )
|
347
|
+
begin
|
348
|
+
require "linguistics/#{code}"
|
349
|
+
rescue LoadError => err
|
350
|
+
msgs << "Tried 'linguistics/#{code}': #{err.message}\n"
|
351
|
+
next
|
352
|
+
end
|
353
|
+
end
|
354
|
+
|
355
|
+
break Linguistics::const_get( code.upcase ) if
|
356
|
+
Linguistics::const_defined?( code.upcase )
|
357
|
+
}
|
358
|
+
|
359
|
+
if mod.is_a?( Array )
|
360
|
+
raise LoadError,
|
361
|
+
"Failed to load language extension %s:\n%s" %
|
362
|
+
[ lang, msgs.join ]
|
363
|
+
end
|
364
|
+
return mod
|
365
|
+
end
|
366
|
+
|
367
|
+
end # class linguistics
|
368
|
+
|
data/redist/crosscase.rb
ADDED
@@ -0,0 +1,298 @@
|
|
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 78 2005-07-13 19:58:43Z ged $
|
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: 78 $} )[1]
|
89
|
+
Rcsid = %q$Id: crosscase.rb 78 2005-07-13 19:58:43Z ged $
|
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 instance_methods( false ).include?( id.to_s ) &&
|
137
|
+
( aliasName = CrossCase::transform(id) )
|
138
|
+
CrossCase::installAlias( self, id, aliasName )
|
139
|
+
end
|
140
|
+
if respond_to?( :__cc_ma )
|
141
|
+
__cc_ma( id )
|
142
|
+
else
|
143
|
+
super
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
}
|
148
|
+
end
|
149
|
+
|
150
|
+
|
151
|
+
### Search for and install aliases for either underbarred or camelCased
|
152
|
+
### class methods for +mod+ (a Class or Module).
|
153
|
+
def self::transformClassMethods( mod )
|
154
|
+
self::findTargetMethods( mod.singleton_methods(false) ) {|meth, aliasName|
|
155
|
+
self::installClassAlias( mod, meth, aliasName )
|
156
|
+
}
|
157
|
+
end
|
158
|
+
|
159
|
+
|
160
|
+
### Install an alias +aliasName+ for the given class method +meth+ of the
|
161
|
+
### Class or Module +mod+.
|
162
|
+
def self::installClassAlias( mod, meth, aliasName )
|
163
|
+
unless mod.respond_to?( aliasName )
|
164
|
+
code = %{
|
165
|
+
class << self; alias_method( :#{aliasName}, :#{meth} ); end
|
166
|
+
}
|
167
|
+
mod.module_eval( code, __FILE__, __LINE__ )
|
168
|
+
end
|
169
|
+
end
|
170
|
+
|
171
|
+
|
172
|
+
### Search for and install aliases for either underbarred or camelCased
|
173
|
+
### instance methods for +mod+ (a Class or Module).
|
174
|
+
def self::transformInstanceMethods( mod )
|
175
|
+
self::findTargetMethods( mod.instance_methods(false) ) {|meth, aliasName|
|
176
|
+
self::installAlias( mod, meth, aliasName )
|
177
|
+
}
|
178
|
+
end
|
179
|
+
|
180
|
+
|
181
|
+
### Install an alias +aliasName+ for the given instance method +meth+ of the
|
182
|
+
### Class or Module +mod+.
|
183
|
+
def self::installAlias( mod, meth, aliasName )
|
184
|
+
unless mod.instance_methods(true).include?( aliasName )
|
185
|
+
mod.module_eval %{alias_method :#{aliasName}, :#{meth}}
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
|
190
|
+
### Find methods in the given +methodList+ which are candidates for
|
191
|
+
### aliasing.
|
192
|
+
def self::findTargetMethods( *methodList )
|
193
|
+
methodList.flatten.each {|meth|
|
194
|
+
next if /(singleton_)?method_added/ =~ meth
|
195
|
+
transformedName = transform( meth ) or next
|
196
|
+
yield( meth, transformedName )
|
197
|
+
}
|
198
|
+
end
|
199
|
+
|
200
|
+
|
201
|
+
### Return an alternate name for the given method id +mid+. If the method id
|
202
|
+
### is an under_barred method, returns a camelCased version, and
|
203
|
+
### vice-versa. If no alternate is called for, returns +nil+.
|
204
|
+
def self::transform( mid )
|
205
|
+
methodName = mid.to_s
|
206
|
+
transformedName = ''
|
207
|
+
|
208
|
+
# camelCased methods
|
209
|
+
if /[A-Z]/.match( methodName ) && !/_/.match( methodName )
|
210
|
+
transformedName = methodName.gsub( /([a-z0-9])([A-Z])/ ) {|match|
|
211
|
+
$1 + '_' + $2
|
212
|
+
}.downcase
|
213
|
+
|
214
|
+
# underbarred_methods
|
215
|
+
elsif !/A-Z/.match( methodName ) && /[a-z0-9]_[a-z]/.match( methodName )
|
216
|
+
transformedName = methodName.gsub( /([a-z0-9])_([a-z])/ ) {|match|
|
217
|
+
$1 + $2.upcase
|
218
|
+
}
|
219
|
+
|
220
|
+
else
|
221
|
+
transformedName = nil
|
222
|
+
end
|
223
|
+
|
224
|
+
return transformedName
|
225
|
+
end
|
226
|
+
|
227
|
+
|
228
|
+
end
|
229
|
+
|
230
|
+
|
231
|
+
if $0 == __FILE__
|
232
|
+
require './utils'
|
233
|
+
include UtilityFunctions
|
234
|
+
$yaml = false
|
235
|
+
|
236
|
+
class Foo #:nodoc:
|
237
|
+
def self::singleton_method_added( id )
|
238
|
+
$stderr.puts "Original sma: Added #{id} to #{self.inspect}"
|
239
|
+
end
|
240
|
+
|
241
|
+
def self::method_added( id )
|
242
|
+
$stderr.puts "Original ma: Added #{id} to #{self.inspect}"
|
243
|
+
end
|
244
|
+
|
245
|
+
def self::classPreCamelMethod
|
246
|
+
"classPreCamelMethod"
|
247
|
+
end
|
248
|
+
|
249
|
+
def self::class_pre_underbarred_method
|
250
|
+
"class_pre_underbarred_method"
|
251
|
+
end
|
252
|
+
|
253
|
+
def preCamelCasedMethod
|
254
|
+
"preCamelCasedMethod"
|
255
|
+
end
|
256
|
+
|
257
|
+
def pre_underbarred_method
|
258
|
+
"pre_underbarred_method"
|
259
|
+
end
|
260
|
+
|
261
|
+
extend CrossCase
|
262
|
+
|
263
|
+
def self::classCamelMethod
|
264
|
+
"classCamelMethod"
|
265
|
+
end
|
266
|
+
|
267
|
+
def self::class_underbarred_method
|
268
|
+
"class_underbarred_method"
|
269
|
+
end
|
270
|
+
|
271
|
+
def camelCasedMethod
|
272
|
+
"camelCasedMethod"
|
273
|
+
end
|
274
|
+
|
275
|
+
def underbarred_method
|
276
|
+
"underbarred_method"
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
f = nil
|
281
|
+
try( "to instantiate Foo" ) { f = Foo::new }
|
282
|
+
%w{classPreCamelMethod class_pre_camel_method
|
283
|
+
class_pre_underbarred_method classPreUnderbarredMethod
|
284
|
+
classCamelMethod class_camel_method
|
285
|
+
class_underbarred_method classUnderbarredMethod
|
286
|
+
}.
|
287
|
+
sort.each {|meth|
|
288
|
+
try( "to call #{meth} on Foo" ) {
|
289
|
+
Foo.send( meth )
|
290
|
+
}
|
291
|
+
}
|
292
|
+
Foo.instance_methods(false).sort.each {|meth|
|
293
|
+
try( "to call #{meth} on the instance of Foo" ) {
|
294
|
+
f.send( meth )
|
295
|
+
}
|
296
|
+
}
|
297
|
+
end
|
298
|
+
|