lafcadio 0.4.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|