Linguistics 1.0.3
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/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
|
+
|