lafcadio 0.4.3 → 0.5.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. data/bin/lafcadio_schema +28 -0
  2. data/lib/lafcadio.rb +3 -4
  3. data/lib/lafcadio.rb~ +3 -4
  4. data/lib/lafcadio/TestSuite.rb +2 -0
  5. data/lib/lafcadio/TestSuite.rb~ +16 -0
  6. data/lib/lafcadio/dateTime.rb +93 -2
  7. data/lib/lafcadio/{dateTime/Month.rb → dateTime.rb~} +33 -33
  8. data/lib/lafcadio/depend.rb +3 -0
  9. data/lib/lafcadio/domain.rb +574 -70
  10. data/lib/lafcadio/domain.rb~ +570 -70
  11. data/lib/lafcadio/mock.rb +92 -2
  12. data/lib/lafcadio/mock.rb~ +93 -0
  13. data/lib/lafcadio/objectField.rb +614 -3
  14. data/lib/lafcadio/objectField.rb~ +618 -0
  15. data/lib/lafcadio/objectStore.rb +662 -19
  16. data/lib/lafcadio/objectStore.rb~ +746 -0
  17. data/lib/lafcadio/query.rb +415 -31
  18. data/lib/lafcadio/query.rb~ +572 -0
  19. data/lib/lafcadio/schema.rb +57 -2
  20. data/lib/lafcadio/test.rb +17 -2
  21. data/lib/lafcadio/{test/LafcadioTestCase.rb → test.rb~} +5 -5
  22. data/lib/lafcadio/test/testconfig.dat +1 -1
  23. data/lib/lafcadio/util.rb +337 -20
  24. metadata +16 -77
  25. data/lib/lafcadio/domain/DomainObject.rb +0 -375
  26. data/lib/lafcadio/domain/DomainObject.rb~ +0 -371
  27. data/lib/lafcadio/domain/MapObject.rb +0 -22
  28. data/lib/lafcadio/domain/ObjectType.rb +0 -80
  29. data/lib/lafcadio/includer.rb +0 -18
  30. data/lib/lafcadio/mock/MockDbBridge.rb +0 -78
  31. data/lib/lafcadio/mock/MockDbBridge.rb~ +0 -74
  32. data/lib/lafcadio/mock/MockObjectStore.rb +0 -20
  33. data/lib/lafcadio/objectField/AutoIncrementField.rb +0 -25
  34. data/lib/lafcadio/objectField/BooleanField.rb +0 -83
  35. data/lib/lafcadio/objectField/DateField.rb +0 -33
  36. data/lib/lafcadio/objectField/DateTimeField.rb +0 -25
  37. data/lib/lafcadio/objectField/DecimalField.rb +0 -41
  38. data/lib/lafcadio/objectField/EmailField.rb +0 -28
  39. data/lib/lafcadio/objectField/EnumField.rb +0 -62
  40. data/lib/lafcadio/objectField/FieldValueError.rb +0 -4
  41. data/lib/lafcadio/objectField/IntegerField.rb +0 -15
  42. data/lib/lafcadio/objectField/LinkField.rb +0 -92
  43. data/lib/lafcadio/objectField/LinkField.rb~ +0 -86
  44. data/lib/lafcadio/objectField/MoneyField.rb +0 -13
  45. data/lib/lafcadio/objectField/MonthField.rb +0 -16
  46. data/lib/lafcadio/objectField/ObjectField.rb +0 -142
  47. data/lib/lafcadio/objectField/PasswordField.rb +0 -29
  48. data/lib/lafcadio/objectField/StateField.rb +0 -13
  49. data/lib/lafcadio/objectField/SubsetLinkField.rb +0 -25
  50. data/lib/lafcadio/objectField/TextField.rb +0 -23
  51. data/lib/lafcadio/objectField/TextListField.rb +0 -21
  52. data/lib/lafcadio/objectField/TimeStampField.rb +0 -15
  53. data/lib/lafcadio/objectStore/Cache.rb +0 -81
  54. data/lib/lafcadio/objectStore/Committer.rb +0 -65
  55. data/lib/lafcadio/objectStore/CouldntMatchObjectTypeError.rb +0 -4
  56. data/lib/lafcadio/objectStore/DbBridge.rb +0 -140
  57. data/lib/lafcadio/objectStore/DbBridge.rb~ +0 -140
  58. data/lib/lafcadio/objectStore/DomainComparable.rb +0 -25
  59. data/lib/lafcadio/objectStore/DomainObjectInitError.rb +0 -9
  60. data/lib/lafcadio/objectStore/DomainObjectNotFoundError.rb +0 -4
  61. data/lib/lafcadio/objectStore/DomainObjectProxy.rb +0 -62
  62. data/lib/lafcadio/objectStore/DomainObjectSqlMaker.rb +0 -74
  63. data/lib/lafcadio/objectStore/ObjectStore.rb +0 -207
  64. data/lib/lafcadio/objectStore/ObjectStore.rb~ +0 -207
  65. data/lib/lafcadio/objectStore/SqlValueConverter.rb +0 -30
  66. data/lib/lafcadio/objectStore/SqlValueConverter.rb~ +0 -30
  67. data/lib/lafcadio/query/Compare.rb +0 -55
  68. data/lib/lafcadio/query/CompoundCondition.rb +0 -39
  69. data/lib/lafcadio/query/Condition.rb +0 -66
  70. data/lib/lafcadio/query/Condition.rb~ +0 -66
  71. data/lib/lafcadio/query/Equals.rb +0 -45
  72. data/lib/lafcadio/query/In.rb +0 -20
  73. data/lib/lafcadio/query/Like.rb +0 -48
  74. data/lib/lafcadio/query/Link.rb +0 -20
  75. data/lib/lafcadio/query/Max.rb +0 -32
  76. data/lib/lafcadio/query/Max.rb~ +0 -25
  77. data/lib/lafcadio/query/Not.rb +0 -21
  78. data/lib/lafcadio/query/Query.rb +0 -92
  79. data/lib/lafcadio/schema/CreateTableStatement.rb +0 -61
  80. data/lib/lafcadio/schema/CreateTableStatement.rb~ +0 -59
  81. data/lib/lafcadio/util/Context.rb +0 -61
  82. data/lib/lafcadio/util/ContextualService.rb +0 -33
  83. data/lib/lafcadio/util/English.rb +0 -117
  84. data/lib/lafcadio/util/HashOfArrays.rb +0 -48
  85. data/lib/lafcadio/util/LafcadioConfig.rb +0 -25
  86. data/lib/lafcadio/util/QueueHash.rb +0 -67
  87. data/lib/lafcadio/util/UsStates.rb +0 -29
  88. data/lib/lafcadio/xml.rb +0 -2
@@ -1,2 +1,57 @@
1
- require 'lafcadio/includer'
2
- Includer.include( 'schema' )
1
+ require 'lafcadio/objectField'
2
+
3
+ module Lafcadio
4
+ class CreateTableStatement #:nodoc:
5
+ @@simple_field_clauses = {
6
+ DecimalField => 'float', DateField => 'date', BooleanField => 'bool',
7
+ TimeStampField => 'timestamp', DateTimeField => 'datetime'
8
+ }
9
+
10
+ def initialize( domain_class )
11
+ @domain_class = domain_class
12
+ end
13
+
14
+ def definition_terms( field )
15
+ definitionTerms = []
16
+ definitionTerms << field.db_field_name
17
+ definitionTerms << type_clause( field )
18
+ definitionTerms << 'not null' if field.not_null
19
+ definitionTerms << 'unique' if field.unique
20
+ definitionTerms.join( ' ' )
21
+ end
22
+
23
+ def to_sql
24
+ createDefinitions = []
25
+ createDefinitions << "#{ @domain_class.sql_primary_key_name } " +
26
+ "int not null auto_increment"
27
+ createDefinitions << "primary key (#{ @domain_class.sql_primary_key_name })"
28
+ @domain_class.class_fields.each { |field|
29
+ createDefinitions << definition_terms( field )
30
+ }
31
+ <<-SQL
32
+ create table #{ @domain_class.table_name } (
33
+ #{ createDefinitions.join(",\n ") }
34
+ );
35
+ SQL
36
+ end
37
+
38
+ def type_clause( field )
39
+ if ( type_clause = @@simple_field_clauses[field.class] )
40
+ type_clause
41
+ elsif ( field.class <= EnumField )
42
+ singleQuotedValues = field.enums.keys.collect! { |enumValue|
43
+ "'#{ enumValue }'"
44
+ }
45
+ "enum( #{ singleQuotedValues.join( ', ' ) } )"
46
+ elsif ( field.class <= TextField || field.class <= TextListField )
47
+ 'varchar(255)'
48
+ elsif ( field.class <= LinkField || field.class <= IntegerField )
49
+ 'int'
50
+ elsif ( field.class <= DecimalField )
51
+ 'float(10, 2)'
52
+ elsif ( field.class <= BlobField )
53
+ 'blob'
54
+ end
55
+ end
56
+ end
57
+ end
@@ -1,2 +1,17 @@
1
- require 'lafcadio/includer'
2
- Includer.include( 'test' )
1
+ require 'lafcadio/depend'
2
+ require 'lafcadio/mock'
3
+ require 'lafcadio/util'
4
+
5
+ # A test case that sets up a number of mock services. In writing an application
6
+ # that uses Lafcadio you may find it convenient to inherit from this class.
7
+ class LafcadioTestCase < RUNIT::TestCase
8
+ include Lafcadio
9
+
10
+ def setup
11
+ context = Context.instance
12
+ context.flush
13
+ @mockObjectStore = MockObjectStore.new
14
+ ObjectStore.set_object_store @mockObjectStore
15
+ LafcadioConfig.set_filename 'lafcadio/test/testConfig.dat'
16
+ end
17
+ end
@@ -1,6 +1,6 @@
1
1
  require 'runit/testcase'
2
- require 'lafcadio/mock/MockObjectStore'
3
- require 'lafcadio/util/LafcadioConfig'
2
+ require 'lafcadio/mock'
3
+ require 'lafcadio/util'
4
4
 
5
5
  # A test case that sets up a number of mock services. In writing an application
6
6
  # that uses Lafcadio you may find it convenient to inherit from this class.
@@ -10,8 +10,8 @@ class LafcadioTestCase < RUNIT::TestCase
10
10
  def setup
11
11
  context = Context.instance
12
12
  context.flush
13
- @mockObjectStore = MockObjectStore.new context
14
- context.setObjectStore @mockObjectStore
15
- LafcadioConfig.setFilename 'lafcadio/test/testConfig.dat'
13
+ @mockObjectStore = MockObjectStore.new
14
+ ObjectStore.set_object_store @mockObjectStore
15
+ LafcadioConfig.set_filename 'lafcadio/test/testConfig.dat'
16
16
  end
17
17
  end
@@ -7,7 +7,7 @@ siteName:Site Name
7
7
  url:http://test.url
8
8
  classpath:lafcadio/
9
9
  logdir:../test/testOutput/
10
- domainDirs:lafcadio/domain/,../test/mock/domain/
10
+ domainDirs:../test/mock/domain/
11
11
  domainFiles:../test/mock/domain.rb
12
12
  logSql:n
13
13
  classDefinitionDir:../test/testData
@@ -1,5 +1,5 @@
1
- require 'lafcadio/includer'
2
- Includer.include( 'util' )
1
+ require 'delegate'
2
+ require 'singleton'
3
3
 
4
4
  class Array
5
5
  # If this array has one element, returns that element; otherwise, raises an
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  class Class < Module
17
17
  # Given a String, returns a class object by the same name.
18
- def self.getClass(className)
18
+ def self.get_class(className)
19
19
  theClass = nil
20
20
  ObjectSpace.each_object(Class) { |aClass|
21
21
  theClass = aClass if aClass.name == className
@@ -30,15 +30,322 @@ class Class < Module
30
30
 
31
31
  # Returns the name of <tt>aClass</tt> itself, stripping off the names of any
32
32
  # containing modules or outer classes.
33
- def bareName
33
+ def bare_name
34
34
  name =~ /::/
35
35
  $' || name
36
36
  end
37
37
  end
38
38
 
39
39
  module Lafcadio
40
+
41
+ # The Context is a singleton object that manages ContextualServices. Each
42
+ # ContextualService is a service that connects in some way to external
43
+ # resources: ObjectStore connects to the database; Emailer connects to SMTP,
44
+ # etc.
45
+ #
46
+ # Context makes it easy to ensure that each ContextualService is only
47
+ # instantiated once, which can be quite useful for services with expensive
48
+ # creation.
49
+ #
50
+ # Furthermore, Context allows you to explicitly set instances for a given
51
+ # service, which can be quite useful in testing. For example, once
52
+ # LafcadioTestCase#setup has an instance of MockObjectStore, it calls
53
+ # context.setObjectStore @mockObjectStore
54
+ # which ensures that any future calls to ObjectStore.getObjectStore will
55
+ # return @mockObjectStore, instead of an instance of ObjectStore connecting
56
+ # test code to a live database.
57
+ class Context
58
+ include Singleton
59
+
60
+ def initialize
61
+ @resources = {}
62
+ @init_procs = {}
63
+ end
64
+
65
+ def create_instance( service_class ) #:nodoc:
66
+ if ( proc = @init_procs[service_class] )
67
+ proc.call
68
+ else
69
+ service_class.new
70
+ end
71
+ end
72
+
73
+ # Flushes all cached ContextualServices.
74
+ def flush
75
+ @resources = {}
76
+ end
77
+
78
+ def get_resource( service_class ) #:nodoc:
79
+ resource = @resources[service_class]
80
+ unless resource
81
+ resource = create_instance( service_class )
82
+ set_resource service_class, resource
83
+ end
84
+ resource
85
+ end
86
+
87
+ def set_init_proc( service_class, proc )
88
+ @init_procs[service_class] = proc
89
+ end
90
+
91
+ def set_resource(service_class, resource) #:nodoc:
92
+ @resources[service_class] = resource
93
+ end
94
+ end
95
+
96
+ # A ContextualService is a service that is managed by the Context.
97
+ # ContextualServices are not instantiated normally. Instead, the instance of
98
+ # such a service may be retrieved by calling the method
99
+ # < class name >.get< class name >
100
+ #
101
+ # For example: ObjectStore.getObjectStore
102
+ class ContextualService
103
+ def self.method_missing( methodId, *args )
104
+ methodName = methodId.id2name
105
+ if methodName =~ /^get_(.*)/ || methodName =~ /^set_(.*)/
106
+ if methodName =~ /^get_(.*)/
107
+ Context.instance.get_resource( self )
108
+ else
109
+ Context.instance.set_resource( self, *args )
110
+ end
111
+ else
112
+ super
113
+ end
114
+ end
115
+
116
+ def self.set_init_proc
117
+ proc = proc { yield }
118
+ Context.instance.set_init_proc( self, proc )
119
+ end
120
+
121
+ # ContextualServices can only be initialized through the Context instance.
122
+ # Note that if you're writing your own initialize method in a child class,
123
+ # you should make sure to call super() or you'll overwrite this behavior.
124
+ def initialize
125
+ regexp = %r{lafcadio/util\.rb.*create_instance}
126
+ unless caller.any? { |line| line =~ regexp }
127
+ raise ArgumentError,
128
+ "#{ self.class.name.to_s } should be instantiated by calling " +
129
+ self.class.name.to_s + ".get_" + self.class.name.camel_case_to_underscore,
130
+ caller
131
+ end
132
+ end
133
+ end
134
+
135
+ # A collection of English-language specific utility methods.
136
+ class English
137
+ # Turns a camel-case string ("camel_case_to_english") to plain English ("camel
138
+ # case to english"). Each word is decapitalized.
139
+ def self.camel_case_to_english(camelCaseStr)
140
+ words = []
141
+ nextCapIndex =(camelCaseStr =~ /[A-Z]/)
142
+ while nextCapIndex != nil
143
+ words << $` if $`.size > 0
144
+ camelCaseStr = $& + $'
145
+ camelCaseStr[0] = camelCaseStr[0..0].downcase
146
+ nextCapIndex =(camelCaseStr =~ /[A-Z]/)
147
+ end
148
+ words << camelCaseStr
149
+ words.join ' '
150
+ end
151
+
152
+ # Turns an English language string into camel case.
153
+ def self.english_to_camel_case(englishStr)
154
+ cc = ""
155
+ englishStr.split.each { |word|
156
+ word = word.capitalize unless cc == ''
157
+ cc = cc += word
158
+ }
159
+ cc
160
+ end
161
+
162
+ # Given a singular noun, returns the plural form.
163
+ def self.plural(singular)
164
+ consonantYPattern = Regexp.new("([^aeiou])y$", Regexp::IGNORECASE)
165
+ if singular =~ consonantYPattern
166
+ singular.gsub consonantYPattern, '\1ies'
167
+ elsif singular =~ /[xs]$/
168
+ singular + "es"
169
+ else
170
+ singular + "s"
171
+ end
172
+ end
173
+
174
+ # Returns the proper noun form of a string by capitalizing most of the
175
+ # words.
176
+ #
177
+ # Examples:
178
+ # English.proper_noun("bosnia and herzegovina") ->
179
+ # "Bosnia and Herzegovina"
180
+ # English.proper_noun("macedonia, the former yugoslav republic of") ->
181
+ # "Macedonia, the Former Yugoslav Republic of"
182
+ # English.proper_noun("virgin islands, u.s.") ->
183
+ # "Virgin Islands, U.S."
184
+ def self.proper_noun(string)
185
+ proper_noun = ""
186
+ while(matchIndex = string =~ /[\. ]/)
187
+ word = string[0..matchIndex-1]
188
+ word = word.capitalize unless [ 'and', 'the', 'of' ].index(word) != nil
189
+ proper_noun += word + $&
190
+ string = string[matchIndex+1..string.length]
191
+ end
192
+ word = string
193
+ word = word.capitalize unless [ 'and', 'the', 'of' ].index(word) != nil
194
+ proper_noun += word
195
+ proper_noun
196
+ end
197
+
198
+ # Given a format for a template sentence, generates the sentence while
199
+ # accounting for details such as pluralization and whether to use "a" or
200
+ # "an".
201
+ # [format] The format string. Format codes are:
202
+ # * %num: Number
203
+ # * %is: Transitive verb. This will be turned into "is" or "are",
204
+ # depending on <tt>number</tt>.
205
+ # * %nam: Name. This will be rendered as either singular or
206
+ # plural, depending on <tt>number</tt>.
207
+ # * %a: Indefinite article. This will be turned into "a" or "an",
208
+ # depending on <tt>name</tt>.
209
+ # [name] The name of the object being described.
210
+ # [number] The number of the objects being describes.
211
+ #
212
+ # Examples:
213
+ # English.sentence("There %is currently %num %nam", "product category",
214
+ # 0) -> "There are currently 0 product categories"
215
+ # English.sentence("There %is currently %num %nam", "product category",
216
+ # 1) -> "There is currently 1 product category"
217
+ # English.sentence("Add %a %nam", "invoice") -> "Add an invoice"
218
+ def self.sentence(format, name, number = 1)
219
+ sentence = format
220
+ sentence.gsub!( /%num/, number.to_s )
221
+ isVerb = number == 1 ? "is" : "are"
222
+ sentence.gsub!( /%is/, isVerb )
223
+ name = English.plural name if number != 1
224
+ sentence.gsub!( /%nam/, name )
225
+ article = starts_with_vowel_sound(name) ? 'an' : 'a'
226
+ sentence.gsub!( /%a/, article )
227
+ sentence
228
+ end
229
+
230
+ def self.singular(plural)
231
+ if plural =~ /(.*)ies/
232
+ $1 + 'y'
233
+ elsif plural =~ /(.*s)es/
234
+ $1
235
+ else
236
+ plural =~ /(.*)s/
237
+ $1
238
+ end
239
+ end
240
+
241
+ # Does this word start with a vowel sound? "User" and "usury" don't, but
242
+ # "ugly" does.
243
+ def self.starts_with_vowel_sound(word)
244
+ uSomethingUMatch = word =~ /^u[^aeiuo][aeiou]/
245
+ # 'user' and 'usury' don't start with a vowel sound
246
+ word =~ /^[aeiou]/ && !uSomethingUMatch
247
+ end
248
+ end
249
+
250
+ # LafcadioConfig is a Hash that takes its data from the config file. You'll
251
+ # have to set the location of that file before using it: Use
252
+ # LafcadioConfig.set_filename.
253
+ #
254
+ # LafcadioConfig expects its data to be colon-delimited, one key-value pair
255
+ # to a line. For example:
256
+ # dbuser:user
257
+ # dbpassword:password
258
+ # dbname:lafcadio_test
259
+ # dbhost:localhost
260
+ class LafcadioConfig < Hash
261
+ @@value_hash = nil
262
+
263
+ def self.set_filename(filename); @@filename = filename; end
264
+
265
+ def self.set_values( value_hash ); @@value_hash = value_hash; end
266
+
267
+ def initialize
268
+ if @@value_hash
269
+ @@value_hash.each { |key, value| self[key] = value }
270
+ else
271
+ File.new( @@filename ).each_line { |line|
272
+ line.chomp =~ /^(.*?):(.*)$/
273
+ self[$1] = $2
274
+ }
275
+ end
276
+ end
277
+ end
278
+
40
279
  class MissingError < RuntimeError
41
280
  end
281
+
282
+ # An ordered hash: Keys are ordered according to when they were inserted.
283
+ class QueueHash < DelegateClass( Array )
284
+ # Creates a QueueHash with all the elements in <tt>array</tt> as keys, and
285
+ # each value initially set to be the same as the corresponding key.
286
+ def self.new_from_array(array)
287
+ new( *( ( array.map { |elt| [ elt, elt ] } ).flatten ) )
288
+ end
289
+
290
+ # Takes an even number of arguments, and sets each odd-numbered argument to
291
+ # correspond to the argument immediately afterward. For example:
292
+ # queueHash = QueueHash.new (1, 2, 3, 4)
293
+ # queueHash[1] => 2
294
+ # queueHash[3] => 4
295
+ def initialize(*values)
296
+ @pairs = []
297
+ 0.step(values.size-1, 2) { |i| @pairs << [ values[i], values[i+1] ] }
298
+ super( @pairs )
299
+ end
300
+
301
+ def ==( otherObj )
302
+ if otherObj.class == QueueHash && otherObj.size == size
303
+ ( 0..size ).all? { |i|
304
+ keys[i] == otherObj.keys[i] && values[i] == otherObj.values[i]
305
+ }
306
+ else
307
+ false
308
+ end
309
+ end
310
+
311
+ def [](key)
312
+ ( pair = @pairs.find { |pair| pair[0] == key } ) ? pair.last : nil
313
+ end
314
+
315
+ def []=(key, value); @pairs << [key, value]; end
316
+
317
+ def each; @pairs.each { |pair| yield pair[0], pair[1] }; end
318
+
319
+ def keys; @pairs.map { |pair| pair[0] }; end
320
+
321
+ def values; @pairs.map { |pair| pair[1] }; end
322
+ end
323
+
324
+ class UsStates
325
+ # Returns a QueueHash of states, with two-letter postal codes as keys and
326
+ # state names as values.
327
+ def self.states
328
+ QueueHash.new( 'AL', 'Alabama', 'AK', 'Alaska', 'AZ', 'Arizona',
329
+ 'AR', 'Arkansas', 'CA', 'California', 'CO', 'Colorado',
330
+ 'CT', 'Connecticut', 'DE', 'Delaware',
331
+ 'DC', 'District of Columbia', 'FL', 'Florida',
332
+ 'GA', 'Georgia', 'HI', 'Hawaii', 'ID', 'Idaho',
333
+ 'IL', 'Illinois', 'IN', 'Indiana', 'IA', 'Iowa',
334
+ 'KS', 'Kansas', 'KY', 'Kentucky', 'LA', 'Louisiana',
335
+ 'ME', 'Maine', 'MD', 'Maryland', 'MA', 'Massachusetts',
336
+ 'MI', 'Michigan', 'MN', 'Minnesota', 'MS', 'Mississippi',
337
+ 'MO', 'Missouri', 'MT', 'Montana', 'NE', 'Nebraska',
338
+ 'NV', 'Nevada', 'NH', 'New Hampshire', 'NJ', 'New Jersey',
339
+ 'NM', 'New Mexico', 'NY', 'New York',
340
+ 'NC', 'North Carolina', 'ND', 'North Dakota', 'OH', 'Ohio',
341
+ 'OK', 'Oklahoma', 'OR', 'Oregon', 'PA', 'Pennsylvania',
342
+ 'PR', 'Puerto Rico', 'RI', 'Rhode Island',
343
+ 'SC', 'South Carolina', 'SD', 'South Dakota',
344
+ 'TN', 'Tennessee', 'TX', 'Texas', 'UT', 'Utah',
345
+ 'VT', 'Vermont', 'VA', 'Virginia', 'WA', 'Washington',
346
+ 'WV', 'West Virginia', 'WI', 'Wisconsin', 'WY', 'Wyoming' )
347
+ end
348
+ end
42
349
  end
43
350
 
44
351
  class Numeric
@@ -47,10 +354,10 @@ class Numeric
47
354
  # <tt>false</tt> and the number rounds to a whole number, there will be no
48
355
  # decimals shown.
49
356
  #
50
- # (24.55).precisionFormat( 3 ) -> "24.550"
51
- # (24.55).precisionFormat( 0 ) -> "24"
52
- # 100.precisionFormat( 2, false ) -> "100"
53
- def precisionFormat(precision, padDecimals = true)
357
+ # (24.55).precision_format( 3 ) -> "24.550"
358
+ # (24.55).precision_format( 0 ) -> "24"
359
+ # 100.precision_format( 2, false ) -> "100"
360
+ def precision_format(precision, padDecimals = true)
54
361
  str = floor.to_s
55
362
  if precision > 0
56
363
  decimal = self - self.floor
@@ -68,8 +375,13 @@ class Numeric
68
375
  end
69
376
 
70
377
  class String
378
+ # Returns the underscored version of a camel-case string.
379
+ def camel_case_to_underscore
380
+ ( gsub( /(.)([A-Z])/ ) { $1 + '_' + $2.downcase } ).downcase
381
+ end
382
+
71
383
  # Returns the number of times that <tt>regexp</tt> occurs in the string.
72
- def countOccurrences(regexp)
384
+ def count_occurrences(regexp)
73
385
  count = 0
74
386
  str = self.clone
75
387
  while str =~ regexp
@@ -102,10 +414,10 @@ class String
102
414
  # that number; otherwise it appends a "_1" after the filename but before the
103
415
  # file extension.
104
416
  #
105
- # "john.jpg".incrementFilename -> "john_1.jpg"
106
- # "john_1.jpg".incrementFilename -> "john_2.jpg"
107
- # "john_2.jpg".incrementFilename -> "john_3.jpg"
108
- def incrementFilename
417
+ # "john.jpg".increment_filename -> "john_1.jpg"
418
+ # "john_1.jpg".increment_filename -> "john_2.jpg"
419
+ # "john_2.jpg".increment_filename -> "john_3.jpg"
420
+ def increment_filename
109
421
  filename = self.clone
110
422
  extension = filename.split(/\./).last
111
423
  filename.sub!(/\..*$/, '')
@@ -121,9 +433,9 @@ class String
121
433
 
122
434
  # Breaks a string into lines no longer than <tt>lineLength</tt>.
123
435
  #
124
- # 'the quick brown fox jumped over the lazy dog.'.lineWrap( 10 ) ->
436
+ # 'the quick brown fox jumped over the lazy dog.'.line_wrape( 10 ) ->
125
437
  # "the quick\nbrown fox\njumped\nover the\nlazy dog."
126
- def lineWrap(lineLength)
438
+ def line_wrape(lineLength)
127
439
  words = split ' '
128
440
  line = ''
129
441
  lines = []
@@ -141,9 +453,9 @@ class String
141
453
  # Turns a numeric string into U.S. format if it's not already formatted that
142
454
  # way.
143
455
  #
144
- # "10,00".numericStringToUsFormat -> "10.00"
145
- # "10.00".numericStringToUsFormat -> "10.00"
146
- def numericStringToUsFormat
456
+ # "10,00".numeric_string_to_us_format -> "10.00"
457
+ # "10.00".numeric_string_to_us_format -> "10.00"
458
+ def numeric_string_to_us_format
147
459
  numericString = clone
148
460
  numericString.gsub!(/,/, '.') if numericString =~ /,\d{2}$/
149
461
  numericString
@@ -164,9 +476,9 @@ class String
164
476
  # delimiter, and returns an array containing both the substrings and the
165
477
  # portions that matched <tt>regexp</tt>.
166
478
  #
167
- # 'theZquickZZbrownZfox'.splitKeepInBetweens(/Z+/) ->
479
+ # 'theZquickZZbrownZfox'.split_keep_in_betweens(/Z+/) ->
168
480
  # ['the', 'Z', 'quick', 'ZZ', 'brown', 'Z', 'fox' ]
169
- def splitKeepInBetweens(regexp)
481
+ def split_keep_in_betweens(regexp)
170
482
  result = []
171
483
  string = clone
172
484
  while string =~ regexp
@@ -177,4 +489,9 @@ class String
177
489
  result << string unless string == ''
178
490
  result
179
491
  end
492
+
493
+ # Returns the camel-case equivalent of an underscore-style string.
494
+ def underscore_to_camel_case
495
+ capitalize.gsub( /_([a-zA-Z0-9]+)/ ) { |s| s[1,s.size - 1].capitalize }
496
+ end
180
497
  end