lafcadio 0.4.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/lib/lafcadio.rb +32 -0
- data/lib/lafcadio.rb~ +32 -0
- data/lib/lafcadio/TestSuite.rb +16 -0
- data/lib/lafcadio/dateTime.rb +2 -0
- data/lib/lafcadio/dateTime/Month.rb +93 -0
- data/lib/lafcadio/domain.rb +119 -0
- data/lib/lafcadio/domain.rb~ +119 -0
- data/lib/lafcadio/domain/DomainObject.rb +375 -0
- data/lib/lafcadio/domain/DomainObject.rb~ +371 -0
- data/lib/lafcadio/domain/MapObject.rb +22 -0
- data/lib/lafcadio/domain/ObjectType.rb +80 -0
- data/lib/lafcadio/includer.rb +18 -0
- data/lib/lafcadio/mock.rb +2 -0
- data/lib/lafcadio/mock/MockDbBridge.rb +78 -0
- data/lib/lafcadio/mock/MockDbBridge.rb~ +74 -0
- data/lib/lafcadio/mock/MockObjectStore.rb +20 -0
- data/lib/lafcadio/objectField.rb +14 -0
- data/lib/lafcadio/objectField/AutoIncrementField.rb +25 -0
- data/lib/lafcadio/objectField/BooleanField.rb +83 -0
- data/lib/lafcadio/objectField/DateField.rb +33 -0
- data/lib/lafcadio/objectField/DateTimeField.rb +25 -0
- data/lib/lafcadio/objectField/DecimalField.rb +41 -0
- data/lib/lafcadio/objectField/EmailField.rb +28 -0
- data/lib/lafcadio/objectField/EnumField.rb +62 -0
- data/lib/lafcadio/objectField/FieldValueError.rb +4 -0
- data/lib/lafcadio/objectField/IntegerField.rb +15 -0
- data/lib/lafcadio/objectField/LinkField.rb +92 -0
- data/lib/lafcadio/objectField/LinkField.rb~ +86 -0
- data/lib/lafcadio/objectField/MoneyField.rb +13 -0
- data/lib/lafcadio/objectField/MonthField.rb +16 -0
- data/lib/lafcadio/objectField/ObjectField.rb +142 -0
- data/lib/lafcadio/objectField/PasswordField.rb +29 -0
- data/lib/lafcadio/objectField/StateField.rb +13 -0
- data/lib/lafcadio/objectField/SubsetLinkField.rb +25 -0
- data/lib/lafcadio/objectField/TextField.rb +23 -0
- data/lib/lafcadio/objectField/TextListField.rb +21 -0
- data/lib/lafcadio/objectField/TimeStampField.rb +15 -0
- data/lib/lafcadio/objectStore.rb +100 -0
- data/lib/lafcadio/objectStore/Cache.rb +81 -0
- data/lib/lafcadio/objectStore/Committer.rb +65 -0
- data/lib/lafcadio/objectStore/CouldntMatchObjectTypeError.rb +4 -0
- data/lib/lafcadio/objectStore/DbBridge.rb +140 -0
- data/lib/lafcadio/objectStore/DbBridge.rb~ +140 -0
- data/lib/lafcadio/objectStore/DomainComparable.rb +25 -0
- data/lib/lafcadio/objectStore/DomainObjectInitError.rb +9 -0
- data/lib/lafcadio/objectStore/DomainObjectNotFoundError.rb +4 -0
- data/lib/lafcadio/objectStore/DomainObjectProxy.rb +62 -0
- data/lib/lafcadio/objectStore/DomainObjectSqlMaker.rb +74 -0
- data/lib/lafcadio/objectStore/ObjectStore.rb +207 -0
- data/lib/lafcadio/objectStore/ObjectStore.rb~ +207 -0
- data/lib/lafcadio/objectStore/SqlValueConverter.rb +30 -0
- data/lib/lafcadio/objectStore/SqlValueConverter.rb~ +30 -0
- data/lib/lafcadio/query.rb +203 -0
- data/lib/lafcadio/query/Compare.rb +55 -0
- data/lib/lafcadio/query/CompoundCondition.rb +39 -0
- data/lib/lafcadio/query/Condition.rb +66 -0
- data/lib/lafcadio/query/Condition.rb~ +66 -0
- data/lib/lafcadio/query/Equals.rb +45 -0
- data/lib/lafcadio/query/In.rb +20 -0
- data/lib/lafcadio/query/Like.rb +48 -0
- data/lib/lafcadio/query/Link.rb +20 -0
- data/lib/lafcadio/query/Max.rb +32 -0
- data/lib/lafcadio/query/Max.rb~ +25 -0
- data/lib/lafcadio/query/Not.rb +21 -0
- data/lib/lafcadio/query/Query.rb +92 -0
- data/lib/lafcadio/schema.rb +2 -0
- data/lib/lafcadio/schema/CreateTableStatement.rb +61 -0
- data/lib/lafcadio/schema/CreateTableStatement.rb~ +59 -0
- data/lib/lafcadio/test.rb +2 -0
- data/lib/lafcadio/test/LafcadioTestCase.rb +17 -0
- data/lib/lafcadio/test/testconfig.dat +13 -0
- data/lib/lafcadio/util.rb +180 -0
- data/lib/lafcadio/util/Context.rb +61 -0
- data/lib/lafcadio/util/ContextualService.rb +33 -0
- data/lib/lafcadio/util/English.rb +117 -0
- data/lib/lafcadio/util/HashOfArrays.rb +48 -0
- data/lib/lafcadio/util/LafcadioConfig.rb +25 -0
- data/lib/lafcadio/util/QueueHash.rb +67 -0
- data/lib/lafcadio/util/UsStates.rb +29 -0
- data/lib/lafcadio/xml.rb +2 -0
- metadata +135 -0
@@ -0,0 +1,92 @@
|
|
1
|
+
module Lafcadio
|
2
|
+
class Query #:nodoc:
|
3
|
+
ASC = 1
|
4
|
+
DESC = 2
|
5
|
+
|
6
|
+
attr_reader :objectType, :condition
|
7
|
+
attr_accessor :orderBy, :orderByOrder, :limit
|
8
|
+
|
9
|
+
def initialize(objectType, pkIdOrCondition = nil)
|
10
|
+
@objectType = objectType
|
11
|
+
( @condition, @orderBy, @limit ) = [ nil, nil, nil ]
|
12
|
+
if pkIdOrCondition
|
13
|
+
if pkIdOrCondition.class <= Condition
|
14
|
+
@condition = pkIdOrCondition
|
15
|
+
else
|
16
|
+
@condition = Query::Equals.new(objectType.sqlPrimaryKeyName,
|
17
|
+
pkIdOrCondition, objectType)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
@orderByOrder = ASC
|
21
|
+
end
|
22
|
+
|
23
|
+
def hash; toSql.hash; end
|
24
|
+
|
25
|
+
def eql?( other )
|
26
|
+
other.class <= Query && other.toSql == toSql
|
27
|
+
end
|
28
|
+
|
29
|
+
def tables
|
30
|
+
tableNames = []
|
31
|
+
anObjectType = objectType
|
32
|
+
until(DomainObject.abstractSubclasses.index(anObjectType) != nil ||
|
33
|
+
anObjectType == DomainObject)
|
34
|
+
tableNames.unshift anObjectType.tableName
|
35
|
+
anObjectType = anObjectType.superclass
|
36
|
+
end
|
37
|
+
tableNames.join ', '
|
38
|
+
end
|
39
|
+
|
40
|
+
def whereClause
|
41
|
+
whereClauses = []
|
42
|
+
anObjectType = objectType
|
43
|
+
superclass = anObjectType.superclass
|
44
|
+
until(DomainObject.abstractSubclasses.index(superclass) != nil ||
|
45
|
+
superclass == DomainObject)
|
46
|
+
joinClause = "#{ sqlPrimaryKeyField(superclass) } = " +
|
47
|
+
"#{ sqlPrimaryKeyField(anObjectType) }"
|
48
|
+
whereClauses.unshift joinClause
|
49
|
+
anObjectType = superclass
|
50
|
+
superclass = superclass.superclass
|
51
|
+
end
|
52
|
+
whereClauses << @condition.toSql if @condition
|
53
|
+
if whereClauses.size > 0
|
54
|
+
"where #{ whereClauses.join(' and ') }"
|
55
|
+
else
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def sqlPrimaryKeyField(objectType)
|
61
|
+
"#{ objectType.tableName }.#{ objectType.sqlPrimaryKeyName }"
|
62
|
+
end
|
63
|
+
|
64
|
+
def fields
|
65
|
+
'*'
|
66
|
+
end
|
67
|
+
|
68
|
+
def orderClause
|
69
|
+
if @orderBy
|
70
|
+
clause = "order by #{ @orderBy } "
|
71
|
+
if @orderByOrder == ASC
|
72
|
+
clause += 'asc'
|
73
|
+
else
|
74
|
+
clause += 'desc'
|
75
|
+
end
|
76
|
+
clause
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def limitClause
|
81
|
+
"limit #{ @limit.begin }, #{ @limit.end - @limit.begin + 1 }" if @limit
|
82
|
+
end
|
83
|
+
|
84
|
+
def toSql
|
85
|
+
clauses = [ "select #{ fields }", "from #{ tables }" ]
|
86
|
+
clauses << whereClause if whereClause
|
87
|
+
clauses << orderClause if orderClause
|
88
|
+
clauses << limitClause if limitClause
|
89
|
+
clauses.join ' '
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
module Lafcadio
|
2
|
+
class CreateTableStatement #:nodoc:
|
3
|
+
def initialize( domainClass )
|
4
|
+
@domainClass = domainClass
|
5
|
+
end
|
6
|
+
|
7
|
+
def typeClause( field )
|
8
|
+
require 'lafcadio/objectField/TextField'
|
9
|
+
require 'lafcadio/objectField/DecimalField'
|
10
|
+
require 'lafcadio/objectField/LinkField'
|
11
|
+
require 'lafcadio/objectField/IntegerField'
|
12
|
+
require 'lafcadio/objectField/DateField'
|
13
|
+
require 'lafcadio/objectField/EnumField'
|
14
|
+
require 'lafcadio/objectField/TextListField'
|
15
|
+
require 'lafcadio/objectField/BooleanField'
|
16
|
+
require 'lafcadio/objectField/DateTimeField'
|
17
|
+
if ( field.class <= EnumField )
|
18
|
+
singleQuotedValues = field.enums.keys.collect! { |enumValue|
|
19
|
+
"'#{ enumValue }'"
|
20
|
+
}
|
21
|
+
"enum( #{ singleQuotedValues.join( ', ' ) } )"
|
22
|
+
elsif ( field.class <= TextField || field.class <= TextListField )
|
23
|
+
'varchar(255)'
|
24
|
+
elsif field.class <= DecimalField
|
25
|
+
"float(10, #{ field.precision })"
|
26
|
+
elsif ( field.class <= LinkField || field.class <= IntegerField )
|
27
|
+
'int'
|
28
|
+
elsif field.class <= DateField
|
29
|
+
'date'
|
30
|
+
elsif field.class <= BooleanField
|
31
|
+
'bool'
|
32
|
+
elsif field.class <= TimeStampField
|
33
|
+
'timestamp'
|
34
|
+
elsif field.class <= DateTimeField
|
35
|
+
'datetime'
|
36
|
+
elsif field.class <= BlobField
|
37
|
+
'blob'
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def toSql
|
42
|
+
createDefinitions = []
|
43
|
+
createDefinitions << "#{ @domainClass.sqlPrimaryKeyName } " +
|
44
|
+
"int not null auto_increment"
|
45
|
+
createDefinitions << "primary key (#{ @domainClass.sqlPrimaryKeyName })"
|
46
|
+
@domainClass.classFields.each { |field|
|
47
|
+
definitionTerms = []
|
48
|
+
definitionTerms << field.dbFieldName
|
49
|
+
definitionTerms << typeClause( field )
|
50
|
+
definitionTerms << 'not null' if field.notNull
|
51
|
+
definitionTerms << 'unique' if field.unique
|
52
|
+
createDefinitions << definitionTerms.join(' ')
|
53
|
+
}
|
54
|
+
<<-SQL
|
55
|
+
create table #{ @domainClass.tableName } (
|
56
|
+
#{ createDefinitions.join(",\n ") }
|
57
|
+
);
|
58
|
+
SQL
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Lafcadio
|
2
|
+
class CreateTableStatement #:nodoc:
|
3
|
+
def initialize( domainClass )
|
4
|
+
@domainClass = domainClass
|
5
|
+
end
|
6
|
+
|
7
|
+
def typeClause( field )
|
8
|
+
require 'lafcadio/objectField/TextField'
|
9
|
+
require 'lafcadio/objectField/DecimalField'
|
10
|
+
require 'lafcadio/objectField/LinkField'
|
11
|
+
require 'lafcadio/objectField/IntegerField'
|
12
|
+
require 'lafcadio/objectField/DateField'
|
13
|
+
require 'lafcadio/objectField/EnumField'
|
14
|
+
require 'lafcadio/objectField/TextListField'
|
15
|
+
require 'lafcadio/objectField/BooleanField'
|
16
|
+
require 'lafcadio/objectField/DateTimeField'
|
17
|
+
if ( field.class <= EnumField )
|
18
|
+
singleQuotedValues = field.enums.keys.collect! { |enumValue|
|
19
|
+
"'#{ enumValue }'"
|
20
|
+
}
|
21
|
+
"enum( #{ singleQuotedValues.join( ', ' ) } )"
|
22
|
+
elsif ( field.class <= TextField || field.class <= TextListField )
|
23
|
+
'varchar(255)'
|
24
|
+
elsif field.class <= DecimalField
|
25
|
+
"float(10, #{ field.precision })"
|
26
|
+
elsif ( field.class <= LinkField || field.class <= IntegerField )
|
27
|
+
'int'
|
28
|
+
elsif field.class <= DateField
|
29
|
+
'date'
|
30
|
+
elsif field.class <= BooleanField
|
31
|
+
'bool'
|
32
|
+
elsif field.class <= TimeStampField
|
33
|
+
'timestamp'
|
34
|
+
elsif field.class <= DateTimeField
|
35
|
+
'datetime'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def toSql
|
40
|
+
createDefinitions = []
|
41
|
+
createDefinitions << "#{ @domainClass.sqlPrimaryKeyName } " +
|
42
|
+
"int not null auto_increment"
|
43
|
+
createDefinitions << "primary key (#{ @domainClass.sqlPrimaryKeyName })"
|
44
|
+
@domainClass.classFields.each { |field|
|
45
|
+
definitionTerms = []
|
46
|
+
definitionTerms << field.dbFieldName
|
47
|
+
definitionTerms << typeClause( field )
|
48
|
+
definitionTerms << 'not null' if field.notNull
|
49
|
+
definitionTerms << 'unique' if field.unique
|
50
|
+
createDefinitions << definitionTerms.join(' ')
|
51
|
+
}
|
52
|
+
<<-SQL
|
53
|
+
create table #{ @domainClass.tableName } (
|
54
|
+
#{ createDefinitions.join(",\n ") }
|
55
|
+
);
|
56
|
+
SQL
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'runit/testcase'
|
2
|
+
require 'lafcadio/mock/MockObjectStore'
|
3
|
+
require 'lafcadio/util/LafcadioConfig'
|
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 context
|
14
|
+
context.setObjectStore @mockObjectStore
|
15
|
+
LafcadioConfig.setFilename 'lafcadio/test/testConfig.dat'
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
dbuser:test
|
2
|
+
dbpassword:password
|
3
|
+
dbname:test
|
4
|
+
dbhost:localhost
|
5
|
+
adminEmail:john.doe@email.com
|
6
|
+
siteName:Site Name
|
7
|
+
url:http://test.url
|
8
|
+
classpath:lafcadio/
|
9
|
+
logdir:../test/testOutput/
|
10
|
+
domainDirs:lafcadio/domain/,../test/mock/domain/
|
11
|
+
domainFiles:../test/mock/domain.rb
|
12
|
+
logSql:n
|
13
|
+
classDefinitionDir:../test/testData
|
@@ -0,0 +1,180 @@
|
|
1
|
+
require 'lafcadio/includer'
|
2
|
+
Includer.include( 'util' )
|
3
|
+
|
4
|
+
class Array
|
5
|
+
# If this array has one element, returns that element; otherwise, raises an
|
6
|
+
# error.
|
7
|
+
def only
|
8
|
+
if size != 1
|
9
|
+
raise "Expected single-value Array but Array has #{ size } members"
|
10
|
+
else
|
11
|
+
first
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Class < Module
|
17
|
+
# Given a String, returns a class object by the same name.
|
18
|
+
def self.getClass(className)
|
19
|
+
theClass = nil
|
20
|
+
ObjectSpace.each_object(Class) { |aClass|
|
21
|
+
theClass = aClass if aClass.name == className
|
22
|
+
}
|
23
|
+
if theClass
|
24
|
+
theClass
|
25
|
+
else
|
26
|
+
raise( Lafcadio::MissingError, "Couldn't find class \"#{ className }\"",
|
27
|
+
caller )
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns the name of <tt>aClass</tt> itself, stripping off the names of any
|
32
|
+
# containing modules or outer classes.
|
33
|
+
def bareName
|
34
|
+
name =~ /::/
|
35
|
+
$' || name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module Lafcadio
|
40
|
+
class MissingError < RuntimeError
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class Numeric
|
45
|
+
# Returns a string that represents the numbeer to <tt>precision</tt> decimal
|
46
|
+
# places, rounding down if necessary. If <tt>padDecimals</tt> is set to
|
47
|
+
# <tt>false</tt> and the number rounds to a whole number, there will be no
|
48
|
+
# decimals shown.
|
49
|
+
#
|
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)
|
54
|
+
str = floor.to_s
|
55
|
+
if precision > 0
|
56
|
+
decimal = self - self.floor
|
57
|
+
decimalStr =(decimal * 10 ** precision).floor.to_s
|
58
|
+
if decimalStr.size < precision
|
59
|
+
decimalStr += "0" *(precision - decimalStr.size)
|
60
|
+
end
|
61
|
+
if padDecimals || decimalStr =~ /[123456789]/
|
62
|
+
str += "."
|
63
|
+
str += decimalStr
|
64
|
+
end
|
65
|
+
end
|
66
|
+
str
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class String
|
71
|
+
# Returns the number of times that <tt>regexp</tt> occurs in the string.
|
72
|
+
def countOccurrences(regexp)
|
73
|
+
count = 0
|
74
|
+
str = self.clone
|
75
|
+
while str =~ regexp
|
76
|
+
count += 1
|
77
|
+
str = $'
|
78
|
+
end
|
79
|
+
count
|
80
|
+
end
|
81
|
+
|
82
|
+
# Decapitalizes the first letter of the string, or decapitalizes the
|
83
|
+
# entire string if it's all capitals.
|
84
|
+
#
|
85
|
+
# 'InternalClient'.decapitalize -> "internalClient"
|
86
|
+
# 'SKU'.decapitalize -> "sku"
|
87
|
+
def decapitalize
|
88
|
+
string = clone
|
89
|
+
firstLetter = string[0..0].downcase
|
90
|
+
string = firstLetter + string[1..string.length]
|
91
|
+
newString = ""
|
92
|
+
while string =~ /([A-Z])([^a-z]|$)/
|
93
|
+
newString += $`
|
94
|
+
newString += $1.downcase
|
95
|
+
string = $2 + $'
|
96
|
+
end
|
97
|
+
newString += string
|
98
|
+
newString
|
99
|
+
end
|
100
|
+
|
101
|
+
# Increments a filename. If the filename ends with a number, it increments
|
102
|
+
# that number; otherwise it appends a "_1" after the filename but before the
|
103
|
+
# file extension.
|
104
|
+
#
|
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
|
109
|
+
filename = self.clone
|
110
|
+
extension = filename.split(/\./).last
|
111
|
+
filename.sub!(/\..*$/, '')
|
112
|
+
if filename =~ /_(\d*)$/
|
113
|
+
newSuffix = $1.to_i + 1
|
114
|
+
filename = $` + "_#{newSuffix}"
|
115
|
+
else
|
116
|
+
filename += "_1"
|
117
|
+
end
|
118
|
+
filename += ".#{extension}"
|
119
|
+
filename
|
120
|
+
end
|
121
|
+
|
122
|
+
# Breaks a string into lines no longer than <tt>lineLength</tt>.
|
123
|
+
#
|
124
|
+
# 'the quick brown fox jumped over the lazy dog.'.lineWrap( 10 ) ->
|
125
|
+
# "the quick\nbrown fox\njumped\nover the\nlazy dog."
|
126
|
+
def lineWrap(lineLength)
|
127
|
+
words = split ' '
|
128
|
+
line = ''
|
129
|
+
lines = []
|
130
|
+
words.each { |word|
|
131
|
+
if line.length + word.length + 1 > lineLength
|
132
|
+
lines << line
|
133
|
+
line = ''
|
134
|
+
end
|
135
|
+
line = line != '' ? "#{ line } #{ word }" : word
|
136
|
+
}
|
137
|
+
lines << line
|
138
|
+
lines.join "\n"
|
139
|
+
end
|
140
|
+
|
141
|
+
# Turns a numeric string into U.S. format if it's not already formatted that
|
142
|
+
# way.
|
143
|
+
#
|
144
|
+
# "10,00".numericStringToUsFormat -> "10.00"
|
145
|
+
# "10.00".numericStringToUsFormat -> "10.00"
|
146
|
+
def numericStringToUsFormat
|
147
|
+
numericString = clone
|
148
|
+
numericString.gsub!(/,/, '.') if numericString =~ /,\d{2}$/
|
149
|
+
numericString
|
150
|
+
end
|
151
|
+
|
152
|
+
# Left-pads a string with +fillChar+ up to +size+ size.
|
153
|
+
#
|
154
|
+
# "a".pad( 10, "+") -> "+++++++++a"
|
155
|
+
def pad(size, fillChar)
|
156
|
+
string = clone
|
157
|
+
while string.length < size
|
158
|
+
string = fillChar + string
|
159
|
+
end
|
160
|
+
string
|
161
|
+
end
|
162
|
+
|
163
|
+
# Divides a string into substrings using <tt>regexp</tt> as a
|
164
|
+
# delimiter, and returns an array containing both the substrings and the
|
165
|
+
# portions that matched <tt>regexp</tt>.
|
166
|
+
#
|
167
|
+
# 'theZquickZZbrownZfox'.splitKeepInBetweens(/Z+/) ->
|
168
|
+
# ['the', 'Z', 'quick', 'ZZ', 'brown', 'Z', 'fox' ]
|
169
|
+
def splitKeepInBetweens(regexp)
|
170
|
+
result = []
|
171
|
+
string = clone
|
172
|
+
while string =~ regexp
|
173
|
+
result << $`
|
174
|
+
result << $&
|
175
|
+
string = $'
|
176
|
+
end
|
177
|
+
result << string unless string == ''
|
178
|
+
result
|
179
|
+
end
|
180
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'singleton'
|
2
|
+
|
3
|
+
module Lafcadio
|
4
|
+
# The Context is a singleton object that manages ContextualServices. Each
|
5
|
+
# ContextualService is a service that connects in some way to external
|
6
|
+
# resources: ObjectStore connects to the database; Emailer connects to SMTP,
|
7
|
+
# etc.
|
8
|
+
#
|
9
|
+
# Context makes it easy to ensure that each ContextualService is only
|
10
|
+
# instantiated once, which can be quite useful for services with expensive
|
11
|
+
# creation.
|
12
|
+
#
|
13
|
+
# Furthermore, Context allows you to explicitly set instances for a given
|
14
|
+
# service, which can be quite useful in testing. For example, once
|
15
|
+
# LafcadioTestCase#setup has an instance of MockObjectStore, it calls
|
16
|
+
# context.setObjectStore @mockObjectStore
|
17
|
+
# which ensures that any future calls to ObjectStore.getObjectStore will
|
18
|
+
# return @mockObjectStore, instead of an instance of ObjectStore connecting
|
19
|
+
# test code to a live database.
|
20
|
+
class Context
|
21
|
+
include Singleton
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
@resources = {}
|
25
|
+
end
|
26
|
+
|
27
|
+
# Flushes all cached ContextualServices.
|
28
|
+
def flush
|
29
|
+
@resources = {}
|
30
|
+
end
|
31
|
+
|
32
|
+
def createInstance(resourceName) #:nodoc:
|
33
|
+
resourceClass = eval resourceName
|
34
|
+
resourceClass.new self
|
35
|
+
end
|
36
|
+
|
37
|
+
def getResource(resourceName) #:nodoc:
|
38
|
+
resource = @resources[resourceName]
|
39
|
+
unless resource
|
40
|
+
resource = createInstance resourceName
|
41
|
+
setResource resourceName, resource
|
42
|
+
end
|
43
|
+
resource
|
44
|
+
end
|
45
|
+
|
46
|
+
def setResource(resourceName, resource) #:nodoc:
|
47
|
+
@resources[resourceName] = resource
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(methId, *args) #:nodoc:
|
51
|
+
methodName = methId.id2name
|
52
|
+
if methodName =~ /^get(.*)$/
|
53
|
+
getResource $1
|
54
|
+
elsif methodName =~ /^set(.*)$/
|
55
|
+
setResource $1, args[0]
|
56
|
+
else
|
57
|
+
super
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|