lafcadio 0.4.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (81) hide show
  1. data/lib/lafcadio.rb +32 -0
  2. data/lib/lafcadio.rb~ +32 -0
  3. data/lib/lafcadio/TestSuite.rb +16 -0
  4. data/lib/lafcadio/dateTime.rb +2 -0
  5. data/lib/lafcadio/dateTime/Month.rb +93 -0
  6. data/lib/lafcadio/domain.rb +119 -0
  7. data/lib/lafcadio/domain.rb~ +119 -0
  8. data/lib/lafcadio/domain/DomainObject.rb +375 -0
  9. data/lib/lafcadio/domain/DomainObject.rb~ +371 -0
  10. data/lib/lafcadio/domain/MapObject.rb +22 -0
  11. data/lib/lafcadio/domain/ObjectType.rb +80 -0
  12. data/lib/lafcadio/includer.rb +18 -0
  13. data/lib/lafcadio/mock.rb +2 -0
  14. data/lib/lafcadio/mock/MockDbBridge.rb +78 -0
  15. data/lib/lafcadio/mock/MockDbBridge.rb~ +74 -0
  16. data/lib/lafcadio/mock/MockObjectStore.rb +20 -0
  17. data/lib/lafcadio/objectField.rb +14 -0
  18. data/lib/lafcadio/objectField/AutoIncrementField.rb +25 -0
  19. data/lib/lafcadio/objectField/BooleanField.rb +83 -0
  20. data/lib/lafcadio/objectField/DateField.rb +33 -0
  21. data/lib/lafcadio/objectField/DateTimeField.rb +25 -0
  22. data/lib/lafcadio/objectField/DecimalField.rb +41 -0
  23. data/lib/lafcadio/objectField/EmailField.rb +28 -0
  24. data/lib/lafcadio/objectField/EnumField.rb +62 -0
  25. data/lib/lafcadio/objectField/FieldValueError.rb +4 -0
  26. data/lib/lafcadio/objectField/IntegerField.rb +15 -0
  27. data/lib/lafcadio/objectField/LinkField.rb +92 -0
  28. data/lib/lafcadio/objectField/LinkField.rb~ +86 -0
  29. data/lib/lafcadio/objectField/MoneyField.rb +13 -0
  30. data/lib/lafcadio/objectField/MonthField.rb +16 -0
  31. data/lib/lafcadio/objectField/ObjectField.rb +142 -0
  32. data/lib/lafcadio/objectField/PasswordField.rb +29 -0
  33. data/lib/lafcadio/objectField/StateField.rb +13 -0
  34. data/lib/lafcadio/objectField/SubsetLinkField.rb +25 -0
  35. data/lib/lafcadio/objectField/TextField.rb +23 -0
  36. data/lib/lafcadio/objectField/TextListField.rb +21 -0
  37. data/lib/lafcadio/objectField/TimeStampField.rb +15 -0
  38. data/lib/lafcadio/objectStore.rb +100 -0
  39. data/lib/lafcadio/objectStore/Cache.rb +81 -0
  40. data/lib/lafcadio/objectStore/Committer.rb +65 -0
  41. data/lib/lafcadio/objectStore/CouldntMatchObjectTypeError.rb +4 -0
  42. data/lib/lafcadio/objectStore/DbBridge.rb +140 -0
  43. data/lib/lafcadio/objectStore/DbBridge.rb~ +140 -0
  44. data/lib/lafcadio/objectStore/DomainComparable.rb +25 -0
  45. data/lib/lafcadio/objectStore/DomainObjectInitError.rb +9 -0
  46. data/lib/lafcadio/objectStore/DomainObjectNotFoundError.rb +4 -0
  47. data/lib/lafcadio/objectStore/DomainObjectProxy.rb +62 -0
  48. data/lib/lafcadio/objectStore/DomainObjectSqlMaker.rb +74 -0
  49. data/lib/lafcadio/objectStore/ObjectStore.rb +207 -0
  50. data/lib/lafcadio/objectStore/ObjectStore.rb~ +207 -0
  51. data/lib/lafcadio/objectStore/SqlValueConverter.rb +30 -0
  52. data/lib/lafcadio/objectStore/SqlValueConverter.rb~ +30 -0
  53. data/lib/lafcadio/query.rb +203 -0
  54. data/lib/lafcadio/query/Compare.rb +55 -0
  55. data/lib/lafcadio/query/CompoundCondition.rb +39 -0
  56. data/lib/lafcadio/query/Condition.rb +66 -0
  57. data/lib/lafcadio/query/Condition.rb~ +66 -0
  58. data/lib/lafcadio/query/Equals.rb +45 -0
  59. data/lib/lafcadio/query/In.rb +20 -0
  60. data/lib/lafcadio/query/Like.rb +48 -0
  61. data/lib/lafcadio/query/Link.rb +20 -0
  62. data/lib/lafcadio/query/Max.rb +32 -0
  63. data/lib/lafcadio/query/Max.rb~ +25 -0
  64. data/lib/lafcadio/query/Not.rb +21 -0
  65. data/lib/lafcadio/query/Query.rb +92 -0
  66. data/lib/lafcadio/schema.rb +2 -0
  67. data/lib/lafcadio/schema/CreateTableStatement.rb +61 -0
  68. data/lib/lafcadio/schema/CreateTableStatement.rb~ +59 -0
  69. data/lib/lafcadio/test.rb +2 -0
  70. data/lib/lafcadio/test/LafcadioTestCase.rb +17 -0
  71. data/lib/lafcadio/test/testconfig.dat +13 -0
  72. data/lib/lafcadio/util.rb +180 -0
  73. data/lib/lafcadio/util/Context.rb +61 -0
  74. data/lib/lafcadio/util/ContextualService.rb +33 -0
  75. data/lib/lafcadio/util/English.rb +117 -0
  76. data/lib/lafcadio/util/HashOfArrays.rb +48 -0
  77. data/lib/lafcadio/util/LafcadioConfig.rb +25 -0
  78. data/lib/lafcadio/util/QueueHash.rb +67 -0
  79. data/lib/lafcadio/util/UsStates.rb +29 -0
  80. data/lib/lafcadio/xml.rb +2 -0
  81. 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,2 @@
1
+ require 'lafcadio/includer'
2
+ Includer.include( 'schema' )
@@ -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,2 @@
1
+ require 'lafcadio/includer'
2
+ Includer.include( 'test' )
@@ -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