lafcadio 0.5.2 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,379 @@
1
+ require 'delegate'
2
+ require 'singleton'
3
+
4
+ module Lafcadio
5
+ # The Context is a singleton object that manages ContextualServices. Each
6
+ # ContextualService is a service that connects in some way to external
7
+ # resources: ObjectStore connects to the database; Emailer connects to SMTP,
8
+ # etc.
9
+ #
10
+ # Context makes it easy to ensure that each ContextualService is only
11
+ # instantiated once, which can be quite useful for services with expensive
12
+ # creation.
13
+ #
14
+ # Furthermore, Context allows you to explicitly set instances for a given
15
+ # service, which can be quite useful in testing. For example, once
16
+ # LafcadioTestCase#setup has an instance of MockObjectStore, it calls
17
+ # context.setObjectStore @mockObjectStore
18
+ # which ensures that any future calls to ObjectStore.getObjectStore will
19
+ # return @mockObjectStore, instead of an instance of ObjectStore connecting
20
+ # test code to a live database.
21
+ class Context
22
+ include Singleton
23
+
24
+ def initialize
25
+ @resources = {}
26
+ @init_procs = {}
27
+ end
28
+
29
+ def create_instance( service_class ) #:nodoc:
30
+ if ( proc = @init_procs[service_class] )
31
+ proc.call
32
+ else
33
+ service_class.new
34
+ end
35
+ end
36
+
37
+ # Flushes all cached ContextualServices.
38
+ def flush
39
+ @resources = {}
40
+ end
41
+
42
+ def get_resource( service_class ) #:nodoc:
43
+ resource = @resources[service_class]
44
+ unless resource
45
+ resource = create_instance( service_class )
46
+ set_resource service_class, resource
47
+ end
48
+ resource
49
+ end
50
+
51
+ def set_init_proc( service_class, proc )
52
+ @init_procs[service_class] = proc
53
+ end
54
+
55
+ def set_resource(service_class, resource) #:nodoc:
56
+ @resources[service_class] = resource
57
+ end
58
+ end
59
+
60
+ # A ContextualService is a service that is managed by the Context.
61
+ # ContextualServices are not instantiated normally. Instead, the instance of
62
+ # such a service may be retrieved by calling the method
63
+ # < class name >.get< class name >
64
+ #
65
+ # For example: ObjectStore.getObjectStore
66
+ class ContextualService
67
+ def self.flush; Context.instance.set_resource( self, nil ); end
68
+
69
+ def self.method_missing( methodId, *args )
70
+ methodName = methodId.id2name
71
+ if methodName =~ /^get_(.*)/ || methodName =~ /^set_(.*)/
72
+ if methodName =~ /^get_(.*)/
73
+ Context.instance.get_resource( self )
74
+ else
75
+ Context.instance.set_resource( self, *args )
76
+ end
77
+ else
78
+ super
79
+ end
80
+ end
81
+
82
+ def self.set_init_proc
83
+ proc = proc { yield }
84
+ Context.instance.set_init_proc( self, proc )
85
+ end
86
+
87
+ # ContextualServices can only be initialized through the Context instance.
88
+ # Note that if you're writing your own initialize method in a child class,
89
+ # you should make sure to call super() or you'll overwrite this behavior.
90
+ def initialize
91
+ regexp = %r{lafcadio/util\.rb.*create_instance}
92
+ unless caller.any? { |line| line =~ regexp }
93
+ raise ArgumentError,
94
+ "#{ self.class.name.to_s } should be instantiated by calling " +
95
+ self.class.name.to_s + ".get_" + self.class.name.camel_case_to_underscore,
96
+ caller
97
+ end
98
+ end
99
+ end
100
+
101
+ # A collection of English-language specific utility methods.
102
+ class English
103
+ # Turns a camel-case string ("camel_case_to_english") to plain English ("camel
104
+ # case to english"). Each word is decapitalized.
105
+ def self.camel_case_to_english(camelCaseStr)
106
+ words = []
107
+ nextCapIndex =(camelCaseStr =~ /[A-Z]/)
108
+ while nextCapIndex != nil
109
+ words << $` if $`.size > 0
110
+ camelCaseStr = $& + $'
111
+ camelCaseStr[0] = camelCaseStr[0..0].downcase
112
+ nextCapIndex =(camelCaseStr =~ /[A-Z]/)
113
+ end
114
+ words << camelCaseStr
115
+ words.join ' '
116
+ end
117
+
118
+ # Turns an English language string into camel case.
119
+ def self.english_to_camel_case(englishStr)
120
+ cc = ""
121
+ englishStr.split.each { |word|
122
+ word = word.capitalize unless cc == ''
123
+ cc = cc += word
124
+ }
125
+ cc
126
+ end
127
+
128
+ # Given a singular noun, returns the plural form.
129
+ def self.plural(singular)
130
+ consonantYPattern = Regexp.new("([^aeiou])y$", Regexp::IGNORECASE)
131
+ if singular =~ consonantYPattern
132
+ singular.gsub consonantYPattern, '\1ies'
133
+ elsif singular =~ /[xs]$/
134
+ singular + "es"
135
+ else
136
+ singular + "s"
137
+ end
138
+ end
139
+
140
+ # Returns the proper noun form of a string by capitalizing most of the
141
+ # words.
142
+ #
143
+ # Examples:
144
+ # English.proper_noun("bosnia and herzegovina") ->
145
+ # "Bosnia and Herzegovina"
146
+ # English.proper_noun("macedonia, the former yugoslav republic of") ->
147
+ # "Macedonia, the Former Yugoslav Republic of"
148
+ # English.proper_noun("virgin islands, u.s.") ->
149
+ # "Virgin Islands, U.S."
150
+ def self.proper_noun(string)
151
+ proper_noun = ""
152
+ while(matchIndex = string =~ /[\. ]/)
153
+ word = string[0..matchIndex-1]
154
+ word = word.capitalize unless [ 'and', 'the', 'of' ].index(word) != nil
155
+ proper_noun += word + $&
156
+ string = string[matchIndex+1..string.length]
157
+ end
158
+ word = string
159
+ word = word.capitalize unless [ 'and', 'the', 'of' ].index(word) != nil
160
+ proper_noun += word
161
+ proper_noun
162
+ end
163
+
164
+ # Given a format for a template sentence, generates the sentence while
165
+ # accounting for details such as pluralization and whether to use "a" or
166
+ # "an".
167
+ # [format] The format string. Format codes are:
168
+ # * %num: Number
169
+ # * %is: Transitive verb. This will be turned into "is" or "are",
170
+ # depending on <tt>number</tt>.
171
+ # * %nam: Name. This will be rendered as either singular or
172
+ # plural, depending on <tt>number</tt>.
173
+ # * %a: Indefinite article. This will be turned into "a" or "an",
174
+ # depending on <tt>name</tt>.
175
+ # [name] The name of the object being described.
176
+ # [number] The number of the objects being describes.
177
+ #
178
+ # Examples:
179
+ # English.sentence("There %is currently %num %nam", "product category",
180
+ # 0) -> "There are currently 0 product categories"
181
+ # English.sentence("There %is currently %num %nam", "product category",
182
+ # 1) -> "There is currently 1 product category"
183
+ # English.sentence("Add %a %nam", "invoice") -> "Add an invoice"
184
+ def self.sentence(format, name, number = 1)
185
+ sentence = format
186
+ sentence.gsub!( /%num/, number.to_s )
187
+ isVerb = number == 1 ? "is" : "are"
188
+ sentence.gsub!( /%is/, isVerb )
189
+ name = English.plural name if number != 1
190
+ sentence.gsub!( /%nam/, name )
191
+ article = starts_with_vowel_sound(name) ? 'an' : 'a'
192
+ sentence.gsub!( /%a/, article )
193
+ sentence
194
+ end
195
+
196
+ def self.singular(plural)
197
+ if plural =~ /(.*)ies/
198
+ $1 + 'y'
199
+ elsif plural =~ /(.*s)es/
200
+ $1
201
+ else
202
+ plural =~ /(.*)s/
203
+ $1
204
+ end
205
+ end
206
+
207
+ # Does this word start with a vowel sound? "User" and "usury" don't, but
208
+ # "ugly" does.
209
+ def self.starts_with_vowel_sound(word)
210
+ uSomethingUMatch = word =~ /^u[^aeiuo][aeiou]/
211
+ # 'user' and 'usury' don't start with a vowel sound
212
+ word =~ /^[aeiou]/ && !uSomethingUMatch
213
+ end
214
+ end
215
+
216
+ # LafcadioConfig is a Hash that takes its data from the config file. You'll
217
+ # have to set the location of that file before using it: Use
218
+ # LafcadioConfig.set_filename.
219
+ #
220
+ # LafcadioConfig expects its data to be colon-delimited, one key-value pair
221
+ # to a line. For example:
222
+ # dbuser:user
223
+ # dbpassword:password
224
+ # dbname:lafcadio_test
225
+ # dbhost:localhost
226
+ class LafcadioConfig < Hash
227
+ @@value_hash = nil
228
+
229
+ def self.set_filename(filename); @@filename = filename; end
230
+
231
+ def self.set_values( value_hash ); @@value_hash = value_hash; end
232
+
233
+ def initialize
234
+ if @@value_hash
235
+ @@value_hash.each { |key, value| self[key] = value }
236
+ else
237
+ File.new( @@filename ).each_line { |line|
238
+ line.chomp =~ /^(.*?):(.*)$/
239
+ self[$1] = $2
240
+ }
241
+ end
242
+ end
243
+ end
244
+
245
+ class MissingError < RuntimeError
246
+ end
247
+
248
+ # An ordered hash: Keys are ordered according to when they were inserted.
249
+ class QueueHash < DelegateClass( Array )
250
+ # Creates a QueueHash with all the elements in <tt>array</tt> as keys, and
251
+ # each value initially set to be the same as the corresponding key.
252
+ def self.new_from_array(array)
253
+ new( *( ( array.map { |elt| [ elt, elt ] } ).flatten ) )
254
+ end
255
+
256
+ # Takes an even number of arguments, and sets each odd-numbered argument to
257
+ # correspond to the argument immediately afterward. For example:
258
+ # queueHash = QueueHash.new (1, 2, 3, 4)
259
+ # queueHash[1] => 2
260
+ # queueHash[3] => 4
261
+ def initialize(*values)
262
+ @pairs = []
263
+ 0.step(values.size-1, 2) { |i| @pairs << [ values[i], values[i+1] ] }
264
+ super( @pairs )
265
+ end
266
+
267
+ def ==( otherObj )
268
+ if otherObj.class == QueueHash && otherObj.size == size
269
+ ( 0..size ).all? { |i|
270
+ keys[i] == otherObj.keys[i] && values[i] == otherObj.values[i]
271
+ }
272
+ else
273
+ false
274
+ end
275
+ end
276
+
277
+ def [](key)
278
+ ( pair = @pairs.find { |pair| pair[0] == key } ) ? pair.last : nil
279
+ end
280
+
281
+ def []=(key, value); @pairs << [key, value]; end
282
+
283
+ def each; @pairs.each { |pair| yield pair[0], pair[1] }; end
284
+
285
+ def keys; @pairs.map { |pair| pair[0] }; end
286
+
287
+ def values; @pairs.map { |pair| pair[1] }; end
288
+ end
289
+
290
+ class UsStates
291
+ # Returns a QueueHash of states, with two-letter postal codes as keys and
292
+ # state names as values.
293
+ def self.states
294
+ QueueHash.new( 'AL', 'Alabama', 'AK', 'Alaska', 'AZ', 'Arizona',
295
+ 'AR', 'Arkansas', 'CA', 'California', 'CO', 'Colorado',
296
+ 'CT', 'Connecticut', 'DE', 'Delaware',
297
+ 'DC', 'District of Columbia', 'FL', 'Florida',
298
+ 'GA', 'Georgia', 'HI', 'Hawaii', 'ID', 'Idaho',
299
+ 'IL', 'Illinois', 'IN', 'Indiana', 'IA', 'Iowa',
300
+ 'KS', 'Kansas', 'KY', 'Kentucky', 'LA', 'Louisiana',
301
+ 'ME', 'Maine', 'MD', 'Maryland', 'MA', 'Massachusetts',
302
+ 'MI', 'Michigan', 'MN', 'Minnesota', 'MS', 'Mississippi',
303
+ 'MO', 'Missouri', 'MT', 'Montana', 'NE', 'Nebraska',
304
+ 'NV', 'Nevada', 'NH', 'New Hampshire', 'NJ', 'New Jersey',
305
+ 'NM', 'New Mexico', 'NY', 'New York',
306
+ 'NC', 'North Carolina', 'ND', 'North Dakota', 'OH', 'Ohio',
307
+ 'OK', 'Oklahoma', 'OR', 'Oregon', 'PA', 'Pennsylvania',
308
+ 'PR', 'Puerto Rico', 'RI', 'Rhode Island',
309
+ 'SC', 'South Carolina', 'SD', 'South Dakota',
310
+ 'TN', 'Tennessee', 'TX', 'Texas', 'UT', 'Utah',
311
+ 'VT', 'Vermont', 'VA', 'Virginia', 'WA', 'Washington',
312
+ 'WV', 'West Virginia', 'WI', 'Wisconsin', 'WY', 'Wyoming' )
313
+ end
314
+ end
315
+ end
316
+
317
+ class String
318
+ # Returns the underscored version of a camel-case string.
319
+ def camel_case_to_underscore
320
+ ( gsub( /(.)([A-Z])/ ) { $1 + '_' + $2.downcase } ).downcase
321
+ end
322
+
323
+ # Returns the number of times that <tt>regexp</tt> occurs in the string.
324
+ def count_occurrences(regexp)
325
+ count = 0
326
+ str = self.clone
327
+ while str =~ regexp
328
+ count += 1
329
+ str = $'
330
+ end
331
+ count
332
+ end
333
+
334
+ # Decapitalizes the first letter of the string, or decapitalizes the
335
+ # entire string if it's all capitals.
336
+ #
337
+ # 'InternalClient'.decapitalize -> "internalClient"
338
+ # 'SKU'.decapitalize -> "sku"
339
+ def decapitalize
340
+ string = clone
341
+ firstLetter = string[0..0].downcase
342
+ string = firstLetter + string[1..string.length]
343
+ newString = ""
344
+ while string =~ /([A-Z])([^a-z]|$)/
345
+ newString += $`
346
+ newString += $1.downcase
347
+ string = $2 + $'
348
+ end
349
+ newString += string
350
+ newString
351
+ end
352
+
353
+ # Turns a numeric string into U.S. format if it's not already formatted that
354
+ # way.
355
+ #
356
+ # "10,00".numeric_string_to_us_format -> "10.00"
357
+ # "10.00".numeric_string_to_us_format -> "10.00"
358
+ def numeric_string_to_us_format
359
+ numericString = clone
360
+ numericString.gsub!(/,/, '.') if numericString =~ /,\d{2}$/
361
+ numericString
362
+ end
363
+
364
+ # Left-pads a string with +fillChar+ up to +size+ size.
365
+ #
366
+ # "a".pad( 10, "+") -> "+++++++++a"
367
+ def pad(size, fillChar)
368
+ string = clone
369
+ while string.length < size
370
+ string = fillChar + string
371
+ end
372
+ string
373
+ end
374
+
375
+ # Returns the camel-case equivalent of an underscore-style string.
376
+ def underscore_to_camel_case
377
+ capitalize.gsub( /_([a-zA-Z0-9]+)/ ) { |s| s[1,s.size - 1].capitalize }
378
+ end
379
+ end
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.1
3
3
  specification_version: 1
4
4
  name: lafcadio
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.2
7
- date: 2004-12-01
6
+ version: 0.6.0
7
+ date: 2005-01-02
8
8
  summary: Lafcadio is an object-relational mapping layer
9
9
  require_paths:
10
10
  - lib
@@ -34,26 +34,20 @@ files:
34
34
  - lib/lafcadio.rb
35
35
  - lib/lafcadio.rb~
36
36
  - lib/lafcadio/dateTime.rb
37
- - lib/lafcadio/dateTime.rb~
38
37
  - lib/lafcadio/depend.rb
39
38
  - lib/lafcadio/domain.rb
40
39
  - lib/lafcadio/domain.rb~
41
40
  - lib/lafcadio/mock.rb
42
- - lib/lafcadio/mock.rb~
43
41
  - lib/lafcadio/objectField.rb
44
- - lib/lafcadio/objectField.rb~
45
42
  - lib/lafcadio/objectStore.rb
46
- - lib/lafcadio/objectStore.rb~
47
43
  - lib/lafcadio/query.rb
48
- - lib/lafcadio/query.rb~
49
44
  - lib/lafcadio/schema.rb
50
45
  - lib/lafcadio/test
51
46
  - lib/lafcadio/test.rb
52
- - lib/lafcadio/test.rb~
53
- - lib/lafcadio/TestSuite.rb
54
- - lib/lafcadio/TestSuite.rb~
55
47
  - lib/lafcadio/util.rb
48
+ - lib/lafcadio/util.rb~
56
49
  - lib/lafcadio/test/testconfig.dat
50
+ - lib/lafcadio/test/testconfig.dat~
57
51
  test_files: []
58
52
  rdoc_options: []
59
53
  extra_rdoc_files: []
@@ -65,6 +59,16 @@ dependencies:
65
59
  - !ruby/object:Gem::Dependency
66
60
  name: log4r
67
61
  version_requirement:
62
+ version_requirements: !ruby/object:Gem::Version::Requirement
63
+ requirements:
64
+ -
65
+ - ">"
66
+ - !ruby/object:Gem::Version
67
+ version: 0.0.0
68
+ version:
69
+ - !ruby/object:Gem::Dependency
70
+ name: extensions
71
+ version_requirement:
68
72
  version_requirements: !ruby/object:Gem::Version::Requirement
69
73
  requirements:
70
74
  -
@@ -1,18 +0,0 @@
1
- require 'lafcadio/depend'
2
-
3
- dir = Dir.new 'lafcadio/'
4
- dir.each { |entry|
5
- if ![ '.', '..', 'CVS' ].index(entry) && entry !~ /~$/
6
- begin
7
- subDirName = entry
8
- subDir = Dir.new "lafcadio/#{ subDirName }"
9
- subDir.each { |entry|
10
- if entry =~ /.rb$/
11
- require "lafcadio/#{subDirName}/#{entry}"
12
- end
13
- }
14
- rescue StandardError
15
- # not a directory, whatev
16
- end
17
- end
18
- }