lafcadio 0.4.3 → 0.5.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/lafcadio_schema +28 -0
- data/lib/lafcadio.rb +3 -4
- data/lib/lafcadio.rb~ +3 -4
- data/lib/lafcadio/TestSuite.rb +2 -0
- data/lib/lafcadio/TestSuite.rb~ +16 -0
- data/lib/lafcadio/dateTime.rb +93 -2
- data/lib/lafcadio/{dateTime/Month.rb → dateTime.rb~} +33 -33
- data/lib/lafcadio/depend.rb +3 -0
- data/lib/lafcadio/domain.rb +574 -70
- data/lib/lafcadio/domain.rb~ +570 -70
- data/lib/lafcadio/mock.rb +92 -2
- data/lib/lafcadio/mock.rb~ +93 -0
- data/lib/lafcadio/objectField.rb +614 -3
- data/lib/lafcadio/objectField.rb~ +618 -0
- data/lib/lafcadio/objectStore.rb +662 -19
- data/lib/lafcadio/objectStore.rb~ +746 -0
- data/lib/lafcadio/query.rb +415 -31
- data/lib/lafcadio/query.rb~ +572 -0
- data/lib/lafcadio/schema.rb +57 -2
- data/lib/lafcadio/test.rb +17 -2
- data/lib/lafcadio/{test/LafcadioTestCase.rb → test.rb~} +5 -5
- data/lib/lafcadio/test/testconfig.dat +1 -1
- data/lib/lafcadio/util.rb +337 -20
- metadata +16 -77
- data/lib/lafcadio/domain/DomainObject.rb +0 -375
- data/lib/lafcadio/domain/DomainObject.rb~ +0 -371
- data/lib/lafcadio/domain/MapObject.rb +0 -22
- data/lib/lafcadio/domain/ObjectType.rb +0 -80
- data/lib/lafcadio/includer.rb +0 -18
- data/lib/lafcadio/mock/MockDbBridge.rb +0 -78
- data/lib/lafcadio/mock/MockDbBridge.rb~ +0 -74
- data/lib/lafcadio/mock/MockObjectStore.rb +0 -20
- data/lib/lafcadio/objectField/AutoIncrementField.rb +0 -25
- data/lib/lafcadio/objectField/BooleanField.rb +0 -83
- data/lib/lafcadio/objectField/DateField.rb +0 -33
- data/lib/lafcadio/objectField/DateTimeField.rb +0 -25
- data/lib/lafcadio/objectField/DecimalField.rb +0 -41
- data/lib/lafcadio/objectField/EmailField.rb +0 -28
- data/lib/lafcadio/objectField/EnumField.rb +0 -62
- data/lib/lafcadio/objectField/FieldValueError.rb +0 -4
- data/lib/lafcadio/objectField/IntegerField.rb +0 -15
- data/lib/lafcadio/objectField/LinkField.rb +0 -92
- data/lib/lafcadio/objectField/LinkField.rb~ +0 -86
- data/lib/lafcadio/objectField/MoneyField.rb +0 -13
- data/lib/lafcadio/objectField/MonthField.rb +0 -16
- data/lib/lafcadio/objectField/ObjectField.rb +0 -142
- data/lib/lafcadio/objectField/PasswordField.rb +0 -29
- data/lib/lafcadio/objectField/StateField.rb +0 -13
- data/lib/lafcadio/objectField/SubsetLinkField.rb +0 -25
- data/lib/lafcadio/objectField/TextField.rb +0 -23
- data/lib/lafcadio/objectField/TextListField.rb +0 -21
- data/lib/lafcadio/objectField/TimeStampField.rb +0 -15
- data/lib/lafcadio/objectStore/Cache.rb +0 -81
- data/lib/lafcadio/objectStore/Committer.rb +0 -65
- data/lib/lafcadio/objectStore/CouldntMatchObjectTypeError.rb +0 -4
- data/lib/lafcadio/objectStore/DbBridge.rb +0 -140
- data/lib/lafcadio/objectStore/DbBridge.rb~ +0 -140
- data/lib/lafcadio/objectStore/DomainComparable.rb +0 -25
- data/lib/lafcadio/objectStore/DomainObjectInitError.rb +0 -9
- data/lib/lafcadio/objectStore/DomainObjectNotFoundError.rb +0 -4
- data/lib/lafcadio/objectStore/DomainObjectProxy.rb +0 -62
- data/lib/lafcadio/objectStore/DomainObjectSqlMaker.rb +0 -74
- data/lib/lafcadio/objectStore/ObjectStore.rb +0 -207
- data/lib/lafcadio/objectStore/ObjectStore.rb~ +0 -207
- data/lib/lafcadio/objectStore/SqlValueConverter.rb +0 -30
- data/lib/lafcadio/objectStore/SqlValueConverter.rb~ +0 -30
- data/lib/lafcadio/query/Compare.rb +0 -55
- data/lib/lafcadio/query/CompoundCondition.rb +0 -39
- data/lib/lafcadio/query/Condition.rb +0 -66
- data/lib/lafcadio/query/Condition.rb~ +0 -66
- data/lib/lafcadio/query/Equals.rb +0 -45
- data/lib/lafcadio/query/In.rb +0 -20
- data/lib/lafcadio/query/Like.rb +0 -48
- data/lib/lafcadio/query/Link.rb +0 -20
- data/lib/lafcadio/query/Max.rb +0 -32
- data/lib/lafcadio/query/Max.rb~ +0 -25
- data/lib/lafcadio/query/Not.rb +0 -21
- data/lib/lafcadio/query/Query.rb +0 -92
- data/lib/lafcadio/schema/CreateTableStatement.rb +0 -61
- data/lib/lafcadio/schema/CreateTableStatement.rb~ +0 -59
- data/lib/lafcadio/util/Context.rb +0 -61
- data/lib/lafcadio/util/ContextualService.rb +0 -33
- data/lib/lafcadio/util/English.rb +0 -117
- data/lib/lafcadio/util/HashOfArrays.rb +0 -48
- data/lib/lafcadio/util/LafcadioConfig.rb +0 -25
- data/lib/lafcadio/util/QueueHash.rb +0 -67
- data/lib/lafcadio/util/UsStates.rb +0 -29
- data/lib/lafcadio/xml.rb +0 -2
data/lib/lafcadio/schema.rb
CHANGED
@@ -1,2 +1,57 @@
|
|
1
|
-
require 'lafcadio/
|
2
|
-
|
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
|
data/lib/lafcadio/test.rb
CHANGED
@@ -1,2 +1,17 @@
|
|
1
|
-
require 'lafcadio/
|
2
|
-
|
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
|
3
|
-
require 'lafcadio/util
|
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
|
14
|
-
|
15
|
-
LafcadioConfig.
|
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
|
10
|
+
domainDirs:../test/mock/domain/
|
11
11
|
domainFiles:../test/mock/domain.rb
|
12
12
|
logSql:n
|
13
13
|
classDefinitionDir:../test/testData
|
data/lib/lafcadio/util.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
require '
|
2
|
-
|
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.
|
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
|
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).
|
51
|
-
# (24.55).
|
52
|
-
# 100.
|
53
|
-
def
|
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
|
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".
|
106
|
-
# "john_1.jpg".
|
107
|
-
# "john_2.jpg".
|
108
|
-
def
|
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.'.
|
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
|
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".
|
145
|
-
# "10.00".
|
146
|
-
def
|
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'.
|
479
|
+
# 'theZquickZZbrownZfox'.split_keep_in_betweens(/Z+/) ->
|
168
480
|
# ['the', 'Z', 'quick', 'ZZ', 'brown', 'Z', 'fox' ]
|
169
|
-
def
|
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
|