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.
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,13 @@
1
+ require 'lafcadio/util/UsStates'
2
+ require 'lafcadio/objectField/EnumField'
3
+
4
+ module Lafcadio
5
+ # A StateField is a specialized subclass of EnumField; its possible values are
6
+ # any of the 50 states of the United States, stored as each state's two-letter
7
+ # postal code.
8
+ class StateField < EnumField
9
+ def initialize(objectType, name = "state", englishName = nil)
10
+ super objectType, name, UsStates.states, englishName
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'lafcadio/objectField/LinkField'
2
+
3
+ module Lafcadio
4
+ class SubsetLinkField < LinkField #:nodoc:
5
+ def SubsetLinkField.instantiationParameters( fieldElt )
6
+ parameters = super( fieldElt )
7
+ parameters['subsetField'] = fieldElt.attributes['subsetField']
8
+ parameters
9
+ end
10
+
11
+ def SubsetLinkField.instantiateWithParameters( domainClass, parameters )
12
+ self.new( domainClass, parameters['linkedType'],
13
+ parameters['subsetField'], parameters['name'],
14
+ parameters['englishName'] )
15
+ end
16
+
17
+ attr_accessor :subsetField
18
+
19
+ def initialize(objectType, linkedType, subsetField,
20
+ name = linkedType.name.downcase, englishName = nil)
21
+ super(objectType, linkedType, name, englishName)
22
+ @subsetField = subsetField
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,23 @@
1
+ require 'lafcadio/objectField/ObjectField'
2
+
3
+ module Lafcadio
4
+ # A TextField is expected to contain a string value.
5
+ class TextField < ObjectField
6
+ attr_accessor :large, :size
7
+
8
+ def initialize(objectType, name, englishName = nil)
9
+ super objectType, name, englishName
10
+ @large = false
11
+ end
12
+
13
+ def valueForSQL(value) #:nodoc:
14
+ if value
15
+ value = value.gsub(/(\\?')/) { |m| m.length == 1 ? "''" : m }
16
+ value = value.gsub(/\\/) { '\\\\' }
17
+ "'#{value}'"
18
+ else
19
+ "null"
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ require 'lafcadio/objectField/ObjectField'
2
+
3
+ module Lafcadio
4
+ # TextListField maps to any String SQL field that tries to represent a
5
+ # quick-and-dirty list with a comma-separated string. It returns an Array.
6
+ # For example, a SQL field with the value "john,bill,dave", then the Ruby
7
+ # field will have the value <tt>[ "john", "bill", "dave" ]</tt>.
8
+ class TextListField < ObjectField
9
+ def valueFromSQL(sqlString, lookupLink = true) #:nodoc:
10
+ if sqlString
11
+ sqlString.split ','
12
+ else
13
+ []
14
+ end
15
+ end
16
+
17
+ def valueForSQL(objectValue) #:nodoc:
18
+ "'" + objectValue.join(',') + "'"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,15 @@
1
+ require 'lafcadio/objectField/DateTimeField'
2
+
3
+ module Lafcadio
4
+ class TimeStampField < DateTimeField #:nodoc:
5
+ def initialize(objectType, name = 'timeStamp', englishName = nil)
6
+ super( objectType, name, englishName )
7
+ @hideDisplay = true
8
+ @notNull = false
9
+ end
10
+
11
+ def dbWillAutomaticallyWrite
12
+ true
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,100 @@
1
+ require 'lafcadio/includer'
2
+ Includer.include( 'objectStore' )
3
+ require 'lafcadio/util/English'
4
+
5
+ module Lafcadio
6
+ class FieldMatchError < StandardError; end
7
+
8
+ class ObjectStore
9
+ class MethodDispatch #:nodoc:
10
+ attr_reader :symbol, :args
11
+
12
+ def initialize( orig_method, maybe_proc, *orig_args )
13
+ @orig_method = orig_method
14
+ @maybe_proc = maybe_proc
15
+ @orig_args = orig_args
16
+ @methodName = orig_method.id2name
17
+ if @methodName =~ /^get(.*)$/
18
+ dispatch_get_method
19
+ else
20
+ raise_no_method_error
21
+ end
22
+ end
23
+
24
+ def dispatch_get_plural
25
+ if @orig_args.size == 0 && @maybe_proc.nil?
26
+ @symbol = :getAll
27
+ @args = [ @domain_class ]
28
+ else
29
+ searchTerm = @orig_args[0]
30
+ fieldName = @orig_args[1]
31
+ if searchTerm.nil? && @maybe_proc.nil? && fieldName.nil?
32
+ msg = "ObjectStore\##{ @orig_method } needs a field name as its " +
33
+ "second argument if its first argument is nil"
34
+ raise( ArgumentError, msg, caller )
35
+ end
36
+ dispatch_get_plural_by_query_block_or_search_term( searchTerm,
37
+ fieldName )
38
+ end
39
+ end
40
+
41
+ def dispatch_get_plural_by_query_block
42
+ inferrer = Query::Inferrer.new( @domain_class ) { |obj|
43
+ @maybe_proc.call( obj )
44
+ }
45
+ @symbol = :getSubset
46
+ @args = [ inferrer.execute ]
47
+ end
48
+
49
+ def dispatch_get_plural_by_query_block_or_search_term( searchTerm,
50
+ fieldName )
51
+ if !@maybe_proc.nil? && searchTerm.nil?
52
+ dispatch_get_plural_by_query_block
53
+ elsif @maybe_proc.nil? && ( !( searchTerm.nil? && fieldName.nil? ) )
54
+ @symbol = :getFiltered
55
+ @args = [ @domain_class.name, searchTerm, fieldName ]
56
+ else
57
+ raise( ArgumentError,
58
+ "Shouldn't send both a query block and a search term",
59
+ caller )
60
+ end
61
+ end
62
+
63
+ def dispatch_get_method
64
+ begin
65
+ dispatch_get_singular
66
+ rescue CouldntMatchObjectTypeError
67
+ objectTypeName = English.singular( method_name_after_get )
68
+ begin
69
+ @domain_class = DomainObject.
70
+ getObjectTypeFromString( objectTypeName )
71
+ dispatch_get_plural
72
+ rescue CouldntMatchObjectTypeError
73
+ raise_no_method_error
74
+ end
75
+ end
76
+ end
77
+
78
+ def dispatch_get_singular
79
+ objectType = DomainObject.
80
+ getObjectTypeFromString( method_name_after_get )
81
+ if @orig_args[0].class <= Integer
82
+ @symbol = :get
83
+ @args = [ objectType, @orig_args[0] ]
84
+ elsif @orig_args[0].class <= DomainObject
85
+ @symbol = :getMapObject
86
+ @args = [ objectType, @orig_args[0], @orig_args[1] ]
87
+ end
88
+ end
89
+
90
+ def method_name_after_get
91
+ @orig_method.id2name =~ /^get(.*)$/
92
+ $1
93
+ end
94
+
95
+ def raise_no_method_error
96
+ raise( NoMethodError, "undefined method '#{ @methodName }'", caller )
97
+ end
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,81 @@
1
+ require 'lafcadio/util/ContextualService'
2
+
3
+ module Lafcadio
4
+ class ObjectStore < ContextualService
5
+ class Cache #:nodoc:
6
+ def initialize( dbBridge )
7
+ @dbBridge = dbBridge
8
+ @objects = {}
9
+ @collections_by_query = {}
10
+ @commit_times = {}
11
+ end
12
+
13
+ def hashByObjectType(objectType)
14
+ unless @objects[objectType]
15
+ @objects[objectType] = {}
16
+ end
17
+ @objects[objectType]
18
+ end
19
+
20
+ # Returns a cached domain object, or nil if none is found.
21
+ def get(objectType, pkId)
22
+ hashByObjectType(objectType)[pkId].clone
23
+ end
24
+
25
+ # Saves a domain object.
26
+ def save(dbObject)
27
+ hashByObjectType(dbObject.objectType)[dbObject.pkId] = dbObject
28
+ flush_collection_cache( dbObject.objectType )
29
+ end
30
+
31
+ def getByQuery( query )
32
+ unless @collections_by_query[query]
33
+ newObjects = @dbBridge.getCollectionByQuery(query)
34
+ newObjects.each { |dbObj| save dbObj }
35
+ @collections_by_query[query] = newObjects.collect { |dobj|
36
+ dobj.pkId
37
+ }
38
+ end
39
+ collection = []
40
+ @collections_by_query[query].each { |pkId|
41
+ dobj = get( query.objectType, pkId )
42
+ collection << dobj if dobj
43
+ }
44
+ collection
45
+ end
46
+
47
+ # Returns an array of all domain objects of a given type.
48
+ def getAll(objectType)
49
+ hashByObjectType(objectType).values.collect { |d_obj| d_obj.clone }
50
+ end
51
+
52
+ # Flushes a domain object.
53
+ def flush(dbObject)
54
+ hashByObjectType(dbObject.objectType).delete dbObject.pkId
55
+ flush_collection_cache( dbObject.objectType )
56
+ end
57
+
58
+ def flush_collection_cache( objectType )
59
+ @collections_by_query.keys.each { |query|
60
+ if query.objectType == objectType
61
+ @collections_by_query.delete( query )
62
+ end
63
+ }
64
+ end
65
+
66
+ def last_commit_time( domain_class, pkId )
67
+ by_domain_class = @commit_times[domain_class]
68
+ by_domain_class ? by_domain_class[pkId] : nil
69
+ end
70
+
71
+ def set_commit_time( d_obj )
72
+ by_domain_class = @commit_times[d_obj.objectType]
73
+ if by_domain_class.nil?
74
+ by_domain_class = {}
75
+ @commit_times[d_obj.objectType] = by_domain_class
76
+ end
77
+ by_domain_class[d_obj.pkId] = Time.now
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,65 @@
1
+ require 'lafcadio/objectStore/ObjectStore'
2
+
3
+ module Lafcadio
4
+ class Committer #:nodoc:
5
+ INSERT = 1
6
+ UPDATE = 2
7
+ DELETE = 3
8
+
9
+ attr_reader :commitType, :dbObject
10
+
11
+ def initialize(dbObject, dbBridge)
12
+ @dbObject = dbObject
13
+ @dbBridge = dbBridge
14
+ @objectStore = Context.instance.getObjectStore
15
+ @commitType = nil
16
+ end
17
+
18
+ def setCommitType
19
+ if @dbObject.delete
20
+ @commitType = DELETE
21
+ elsif @dbObject.pkId
22
+ @commitType = UPDATE
23
+ else
24
+ @commitType = INSERT
25
+ end
26
+ end
27
+
28
+ def execute
29
+ setCommitType
30
+ @dbObject.lastCommit = getLastCommit
31
+ @dbObject.preCommitTrigger
32
+ if @dbObject.delete
33
+ dependentClasses = @dbObject.objectType.dependentClasses
34
+ dependentClasses.keys.each { |aClass|
35
+ field = dependentClasses[aClass]
36
+ collection = @objectStore.getFiltered( aClass.name, @dbObject,
37
+ field.name )
38
+ collection.each { |dependentObject|
39
+ if field.deleteCascade
40
+ dependentObject.delete = true
41
+ else
42
+ dependentObject.send( field.name + '=', nil )
43
+ end
44
+ @objectStore.commit(dependentObject)
45
+ }
46
+ }
47
+ end
48
+ @dbBridge.commit @dbObject
49
+ unless @dbObject.pkId
50
+ @dbObject.pkId = @dbBridge.lastPkIdInserted
51
+ end
52
+ @dbObject.postCommitTrigger
53
+ end
54
+
55
+ def getLastCommit
56
+ if @dbObject.delete
57
+ DomainObject::COMMIT_DELETE
58
+ elsif @dbObject.pkId
59
+ DomainObject::COMMIT_EDIT
60
+ else
61
+ DomainObject::COMMIT_ADD
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,4 @@
1
+ module Lafcadio
2
+ class CouldntMatchObjectTypeError < RuntimeError #:nodoc:
3
+ end
4
+ end
@@ -0,0 +1,140 @@
1
+ require 'rubygems'
2
+
3
+ require 'dbi'
4
+ require_gem 'log4r'
5
+ require 'lafcadio/util/LafcadioConfig'
6
+
7
+ module Lafcadio
8
+ class DbBridge #:nodoc:
9
+ @@dbh = nil
10
+ @@lastPkIdInserted = nil
11
+ @@dbName = nil
12
+ @@connectionClass = DBI
13
+
14
+ def DbBridge.setDbName(dbName)
15
+ @@dbName = dbName
16
+ end
17
+
18
+ def DbBridge._load(aString)
19
+ aString =~ /dbh:/
20
+ dbString = $'
21
+ begin
22
+ dbh = Marshal.load(dbString)
23
+ rescue TypeError
24
+ dbh = nil
25
+ end
26
+ new dbh
27
+ end
28
+
29
+ def DbBridge.flushConnection
30
+ @@dbh = nil
31
+ end
32
+
33
+ def DbBridge.setConnectionClass( aClass )
34
+ @@connectionClass = aClass
35
+ end
36
+
37
+ def DbBridge.disconnect
38
+ @@dbh.disconnect if @@dbh
39
+ end
40
+
41
+ def initialize(dbh = nil)
42
+ if dbh == nil
43
+ if @@dbh == nil
44
+ config = LafcadioConfig.new
45
+ dbName = @@dbName || config['dbname']
46
+ dbAndHost = nil
47
+ if dbName && config['dbhost']
48
+ dbAndHost = "dbi:Mysql:#{ dbName }:#{ config['dbhost'] }"
49
+ else
50
+ dbAndHost = "dbi:#{config['dbconn']}"
51
+ end
52
+ @@dbh = @@connectionClass.connect( dbAndHost, config['dbuser'],
53
+ config['dbpassword'] )
54
+ end
55
+ else
56
+ @@dbh = dbh
57
+ end
58
+ @dbh = @@dbh
59
+ ObjectSpace.define_finalizer( self, proc { |id| DbBridge.disconnect } )
60
+ end
61
+
62
+ def maybeLog(sql)
63
+ config = LafcadioConfig.new
64
+ if config['logSql'] == 'y'
65
+ sqllog = Log4r::Logger['sql'] || Log4r::Logger.new( 'sql' )
66
+ filename = File.join( config['logdir'], config['sqlLogFile'] || 'sql' )
67
+ outputter = Log4r::FileOutputter.new( 'outputter',
68
+ { :filename => filename } )
69
+ sqllog.outputters = outputter
70
+ sqllog.info sql
71
+ end
72
+ end
73
+
74
+ def commit(dbObject)
75
+ require 'lafcadio/objectStore/DomainObjectSqlMaker'
76
+ sqlMaker = DomainObjectSqlMaker.new(dbObject)
77
+ sqlMaker.sqlStatements.each { |sql, binds| executeCommit( sql, binds ) }
78
+ if sqlMaker.sqlStatements[0].first =~ /insert/
79
+ sql = 'select last_insert_id()'
80
+ result = executeSelect( sql )
81
+ @@lastPkIdInserted = result[0]['last_insert_id()'].to_i
82
+ end
83
+ end
84
+
85
+ def executeCommit( sql, binds )
86
+ @dbh.do( sql, *binds )
87
+ end
88
+
89
+ def getCollectionByQuery(query)
90
+ require 'lafcadio/objectStore/SqlValueConverter'
91
+ objectType = query.objectType
92
+ coll = []
93
+ objects = []
94
+ result = executeSelect query.toSql
95
+ result.each { |row_hash|
96
+ converter = SqlValueConverter.new(objectType, row_hash)
97
+ obj = objectType.new converter
98
+ objects << obj
99
+ }
100
+ coll = coll.concat objects
101
+ coll
102
+ end
103
+
104
+ def executeSelect(sql)
105
+ maybeLog sql
106
+ begin
107
+ @dbh.select_all( sql )
108
+ rescue DBI::DatabaseError => e
109
+ raise $!.to_s + ": #{ e.errstr }"
110
+ end
111
+ end
112
+
113
+ def _dump(aDepth)
114
+ if @db.respond_to? '_dump'
115
+ dbDump = @db._dump
116
+ else
117
+ dbDump = @db.class.to_s
118
+ end
119
+ "dbh:#{dbDump}"
120
+ end
121
+
122
+ def lastPkIdInserted
123
+ @@lastPkIdInserted
124
+ end
125
+
126
+ def group_query( query )
127
+ row = executeSelect( query.toSql )[0]
128
+ result = []
129
+ row.each { |val|
130
+ if query.field_name != query.objectType.sqlPrimaryKeyName
131
+ a_field = query.objectType.getField( query.field_name )
132
+ result << a_field.valueFromSQL( val )
133
+ else
134
+ result << val.to_i
135
+ end
136
+ }
137
+ result
138
+ end
139
+ end
140
+ end