lafcadio 0.5.2 → 0.6.0

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.
@@ -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
- }