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,207 @@
|
|
1
|
+
require 'lafcadio'
|
2
|
+
|
3
|
+
module Lafcadio
|
4
|
+
# The ObjectStore represents the database in a Lafcadio application.
|
5
|
+
#
|
6
|
+
# = Configuring the ObjectStore
|
7
|
+
# The ObjectStore depends on a few values being set correctly in the
|
8
|
+
# LafcadioConfig file:
|
9
|
+
# [dbuser] The database username.
|
10
|
+
# [dbpassword] The database password.
|
11
|
+
# [dbname] The database name.
|
12
|
+
# [dbhost] The database host.
|
13
|
+
#
|
14
|
+
# = Instantiating ObjectStore
|
15
|
+
# The ObjectStore is a ContextualService, meaning you can't get an instance by
|
16
|
+
# calling ObjectStore.new. Instead, you should call
|
17
|
+
# ObjectStore.getObjectStore. (Using a ContextualService makes it easier to
|
18
|
+
# make out the ObjectStore for unit tests: See ContextualService for more.)
|
19
|
+
#
|
20
|
+
# = Dynamic method calls
|
21
|
+
# ObjectStore uses reflection to provide a lot of convenience methods for
|
22
|
+
# querying domain objects in a number of ways.
|
23
|
+
# [ObjectStore#get< domain class > (pkId)]
|
24
|
+
# Retrieves one domain object by pkId. For example,
|
25
|
+
# ObjectStore#getUser( 100 )
|
26
|
+
# will return User 100.
|
27
|
+
# [ObjectStore#get< domain class >s (searchTerm, fieldName = nil)]
|
28
|
+
# Returns a collection of all instances of that domain class matching that
|
29
|
+
# search term. For example,
|
30
|
+
# ObjectStore#getProducts( aProductCategory )
|
31
|
+
# queries MySQL for all products that belong to that product category. You
|
32
|
+
# can omit +fieldName+ if +searchTerm+ is a non-nil domain object, and the
|
33
|
+
# field connecting the first domain class to the second is named after the
|
34
|
+
# domain class. (For example, the above line assumes that Product has a
|
35
|
+
# field named "productCategory".) Otherwise, it's best to include
|
36
|
+
# +fieldName+:
|
37
|
+
# ObjectStore#getUsers( "Jones", "lastName" )
|
38
|
+
#
|
39
|
+
# = Querying
|
40
|
+
# ObjectStore can also be used to generate complex, ad-hoc queries which
|
41
|
+
# emulate much of the functionality you'd get from writing the SQL yourself.
|
42
|
+
# Furthermore, these queries can be run against in-memory data stores, which
|
43
|
+
# is particularly useful for tests.
|
44
|
+
# date = Date.new( 2003, 1, 1 )
|
45
|
+
# ObjectStore#getInvoices { |invoice|
|
46
|
+
# Query.And( invoice.date.gte( date ), invoice.rate.equals( 10 ),
|
47
|
+
# invoice.hours.equals( 10 ) )
|
48
|
+
# }
|
49
|
+
# is the same as
|
50
|
+
# select * from invoices
|
51
|
+
# where (date >= '2003-01-01' and rate = 10 and hours = 10)
|
52
|
+
# See lafcadio/query.rb for more.
|
53
|
+
#
|
54
|
+
# = SQL Logging
|
55
|
+
# Lafcadio uses log4r to log all of its SQL statements. The simplest way to
|
56
|
+
# turn on logging is to set the following values in the LafcadioConfig file:
|
57
|
+
# [logSql] Should be set to "y" to turn on logging.
|
58
|
+
# [logdir] The directory where log files should be written. Required if
|
59
|
+
# +logSql+ is "y"
|
60
|
+
# [sqlLogFile] The name of the file (not including its directory) where SQL
|
61
|
+
# should be logged. Default is "sql".
|
62
|
+
#
|
63
|
+
# = Triggers
|
64
|
+
# Domain classes can be set to fire triggers either before or after commits.
|
65
|
+
# Since these triggers are executed in Ruby, they're easy to test. See
|
66
|
+
# DomainObject#preCommitTrigger and DomainObject#postCommitTrigger for more.
|
67
|
+
class ObjectStore < ContextualService
|
68
|
+
def ObjectStore.setDbName(dbName) #:nodoc:
|
69
|
+
DbBridge.setDbName dbName
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize(context, dbBridge = nil) #:nodoc:
|
73
|
+
super context
|
74
|
+
@dbBridge = dbBridge == nil ? DbBridge.new : dbBridge
|
75
|
+
@cache = ObjectStore::Cache.new( @dbBridge )
|
76
|
+
end
|
77
|
+
|
78
|
+
# Commits a domain object to the database. You can also simply call
|
79
|
+
# myDomainObject.commit
|
80
|
+
def commit(dbObject)
|
81
|
+
require 'lafcadio/objectStore/Committer'
|
82
|
+
committer = Committer.new dbObject, @dbBridge
|
83
|
+
committer.execute
|
84
|
+
updateCacheAfterCommit( committer )
|
85
|
+
end
|
86
|
+
|
87
|
+
# Flushes one domain object from its cache.
|
88
|
+
def flush(dbObject)
|
89
|
+
@cache.flush dbObject
|
90
|
+
end
|
91
|
+
|
92
|
+
# Returns the domain object corresponding to the domain class and pkId.
|
93
|
+
def get(objectType, pkId)
|
94
|
+
query = Query.new objectType, pkId
|
95
|
+
@cache.getByQuery( query )[0] ||
|
96
|
+
( raise( DomainObjectNotFoundError,
|
97
|
+
"Can't find #{objectType} #{pkId}", caller ) )
|
98
|
+
end
|
99
|
+
|
100
|
+
# Returns all domain objects for the given domain class.
|
101
|
+
def getAll(objectType)
|
102
|
+
query = Query.new( objectType )
|
103
|
+
@cache.getByQuery( query )
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the DbBridge; this is useful in case you need to use raw SQL for a
|
107
|
+
# specific query.
|
108
|
+
def getDbBridge; @dbBridge; end
|
109
|
+
|
110
|
+
def getFiltered(objectTypeName, searchTerm, fieldName = nil) #:nodoc:
|
111
|
+
require 'lafcadio/query/Link'
|
112
|
+
objectType = DomainObject.getObjectTypeFromString objectTypeName
|
113
|
+
unless fieldName
|
114
|
+
fieldName = searchTerm.objectType.bareName
|
115
|
+
fieldName = fieldName.decapitalize
|
116
|
+
end
|
117
|
+
if searchTerm.class <= DomainObject
|
118
|
+
condition = Query::Link.new(fieldName, searchTerm, objectType)
|
119
|
+
else
|
120
|
+
condition = Query::Equals.new(fieldName, searchTerm, objectType)
|
121
|
+
end
|
122
|
+
getSubset( condition )
|
123
|
+
end
|
124
|
+
|
125
|
+
def getMapMatch(objectType, mapped) #:nodoc:
|
126
|
+
fieldName = mapped.objectType.bareName.decapitalize
|
127
|
+
Query::Equals.new(fieldName, mapped, objectType)
|
128
|
+
end
|
129
|
+
|
130
|
+
def getMapObject(objectType, map1, map2) #:nodoc:
|
131
|
+
require 'lafcadio/query/CompoundCondition'
|
132
|
+
unless map1 && map2
|
133
|
+
raise ArgumentError,
|
134
|
+
"ObjectStore#getMapObject needs two non-nil keys", caller
|
135
|
+
end
|
136
|
+
mapMatch1 = getMapMatch objectType, map1
|
137
|
+
mapMatch2 = getMapMatch objectType, map2
|
138
|
+
condition = Query::CompoundCondition.new mapMatch1, mapMatch2
|
139
|
+
getSubset(condition)[0]
|
140
|
+
end
|
141
|
+
|
142
|
+
def getMapped(searchTerm, resultTypeName) #:nodoc:
|
143
|
+
resultType = DomainObject.getObjectTypeFromString resultTypeName
|
144
|
+
coll = []
|
145
|
+
firstTypeName = searchTerm.class.bareName
|
146
|
+
secondTypeName = resultType.bareName
|
147
|
+
mapTypeName = firstTypeName + secondTypeName
|
148
|
+
getFiltered(mapTypeName, searchTerm).each { |mapObj|
|
149
|
+
coll << mapObj.send( resultType.name.decapitalize )
|
150
|
+
}
|
151
|
+
coll
|
152
|
+
end
|
153
|
+
|
154
|
+
# Retrieves the maximum value across all instances of one domain class.
|
155
|
+
# ObjectStore#getMax( Client )
|
156
|
+
# returns the highest +pkId+ in the +clients+ table.
|
157
|
+
# ObjectStore#getMax( Invoice, "rate" )
|
158
|
+
# will return the highest rate for all invoices.
|
159
|
+
def getMax( domain_class, field_name = 'pkId' )
|
160
|
+
query = Query::Max.new( domain_class, field_name )
|
161
|
+
@dbBridge.group_query( query ).only
|
162
|
+
end
|
163
|
+
|
164
|
+
# Retrieves a collection of domain objects by +pkId+.
|
165
|
+
# ObjectStore#getObjects( Clients, [ 1, 2, 3 ] )
|
166
|
+
def getObjects(objectType, pkIds)
|
167
|
+
require 'lafcadio/query/In'
|
168
|
+
condition = Query::In.new('pkId', pkIds, objectType)
|
169
|
+
getSubset condition
|
170
|
+
end
|
171
|
+
|
172
|
+
def getSubset(conditionOrQuery) #:nodoc:
|
173
|
+
if conditionOrQuery.class <= Query::Condition
|
174
|
+
condition = conditionOrQuery
|
175
|
+
query = Query.new condition.objectType, condition
|
176
|
+
else
|
177
|
+
query = conditionOrQuery
|
178
|
+
end
|
179
|
+
@cache.getByQuery( query )
|
180
|
+
end
|
181
|
+
|
182
|
+
def last_commit_time( domain_class, pkId ) #:nodoc:
|
183
|
+
@cache.last_commit_time( domain_class, pkId )
|
184
|
+
end
|
185
|
+
|
186
|
+
def method_missing(methodId, *args) #:nodoc:
|
187
|
+
proc = block_given? ? ( proc { |obj| yield( obj ) } ) : nil
|
188
|
+
dispatch = MethodDispatch.new( methodId, proc, *args )
|
189
|
+
self.send( dispatch.symbol, *dispatch.args )
|
190
|
+
end
|
191
|
+
|
192
|
+
# Caches one domain object.
|
193
|
+
def set(dbObject)
|
194
|
+
@cache.save dbObject
|
195
|
+
end
|
196
|
+
|
197
|
+
def updateCacheAfterCommit( committer ) #:nodoc:
|
198
|
+
if committer.commitType == Committer::UPDATE ||
|
199
|
+
committer.commitType == Committer::INSERT
|
200
|
+
set( committer.dbObject )
|
201
|
+
elsif committer.commitType == Committer::DELETE
|
202
|
+
@cache.flush( committer.dbObject )
|
203
|
+
end
|
204
|
+
@cache.set_commit_time( committer.dbObject )
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lafcadio
|
2
|
+
class SqlValueConverter #:nodoc:
|
3
|
+
attr_reader :objectType, :rowHash
|
4
|
+
|
5
|
+
def initialize(objectType, rowHash)
|
6
|
+
@objectType = objectType
|
7
|
+
@rowHash = rowHash
|
8
|
+
end
|
9
|
+
|
10
|
+
def []( key )
|
11
|
+
if key == 'pkId'
|
12
|
+
if ( field_val = @rowHash[@objectType.sqlPrimaryKeyName] ).nil?
|
13
|
+
error_msg = "The field \"" + @objectType.sqlPrimaryKeyName +
|
14
|
+
"\" can\'t be found in the table \"" +
|
15
|
+
@objectType.tableName + "\"."
|
16
|
+
raise FieldMatchError, error_msg, caller
|
17
|
+
else
|
18
|
+
field_val.to_i
|
19
|
+
end
|
20
|
+
else
|
21
|
+
begin
|
22
|
+
field = @objectType.getField( key )
|
23
|
+
field.valueFromSQL( @rowHash[ field.dbFieldName ] )
|
24
|
+
rescue MissingError
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Lafcadio
|
2
|
+
class SqlValueConverter #:nodoc:
|
3
|
+
attr_reader :objectType, :rowHash
|
4
|
+
|
5
|
+
def initialize(objectType, rowHash)
|
6
|
+
@objectType = objectType
|
7
|
+
@rowHash = rowHash
|
8
|
+
end
|
9
|
+
|
10
|
+
def []( key )
|
11
|
+
if key == 'pkId'
|
12
|
+
if ( field_val = @rowHash[@objectType.sqlPrimaryKeyName] ).nil?
|
13
|
+
error_msg = "The field \"" + @objectType.sqlPrimaryKeyName +
|
14
|
+
"\" can\'t be found in the table \"" +
|
15
|
+
@objectType.tableName + "\"."
|
16
|
+
raise FieldMatchError, error_msg, caller
|
17
|
+
else
|
18
|
+
field_val.to_i
|
19
|
+
end
|
20
|
+
else
|
21
|
+
begin
|
22
|
+
field = @objectType.getField( key )
|
23
|
+
field.valueFromSQL( @rowHash[ key ] )
|
24
|
+
rescue MissingError
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
# = Overview
|
2
|
+
# By passing a block to ObjectStore, you can write complex, ad-hoc queries in
|
3
|
+
# Ruby. This involves a few more keystrokes than writing raw SQL, but also makes
|
4
|
+
# it easier to change queries at runtime, and these queries can also be fully
|
5
|
+
# tested against the MockObjectStore.
|
6
|
+
# big_invoices = object_store.getInvoices { |inv| inv.rate.gt( 50 ) }
|
7
|
+
# # => "select * from invoices where rate > 50"
|
8
|
+
# This a full-fledged block, so you can pass in values from the calling context.
|
9
|
+
# date = Date.new( 2004, 1, 1 )
|
10
|
+
# recent_invoices = object_store.getInvoices { |inv| inv.date.gt( date ) }
|
11
|
+
# # => "select * from invoices where date > '2004-01-01'"
|
12
|
+
#
|
13
|
+
# = Query operators
|
14
|
+
# You can compare fields either to simple values, or to other fields in the same
|
15
|
+
# table.
|
16
|
+
# paid_immediately = object_store.getInvoices { |inv|
|
17
|
+
# inv.date.equals( inv.paid )
|
18
|
+
# }
|
19
|
+
# # => "select * from invoices where date = paid"
|
20
|
+
#
|
21
|
+
# == Numerical comparisons: +lt+, +lte+, +gte+, +gt+
|
22
|
+
# +lt+, +lte+, +gte+, and +gt+ stand for "less than", "less than or equal",
|
23
|
+
# "greater than or equal", and "greater than", respectively.
|
24
|
+
# tiny_invoices = object_store.getInvoices { |inv| inv.rate.lte( 25 ) }
|
25
|
+
# # => "select * from invoices where rate <= 25"
|
26
|
+
# These comparators work on fields that contain numbers, dates, and even
|
27
|
+
# references to other domain objects.
|
28
|
+
# for_1st_ten_clients = object_store.getInvoices { |inv|
|
29
|
+
# inv.client.lte( 10 )
|
30
|
+
# }
|
31
|
+
# # => "select * from invoices where client <= 10"
|
32
|
+
#
|
33
|
+
# == Equality: +equals+
|
34
|
+
# full_week_invs = object_store.getInvoices { |inv| inv.hours.equals( 40 ) }
|
35
|
+
# # => "select * from invoices where hours = 40"
|
36
|
+
# If you're comparing to a domain object you should pass in the object itself.
|
37
|
+
# client = object_store.getClient( 99 )
|
38
|
+
# invoices = object_store.getInvoices { |inv| inv.client.equals( client ) }
|
39
|
+
# # => "select * from invoices where client = 99"
|
40
|
+
#
|
41
|
+
# == Inclusion: +in+
|
42
|
+
# first_three_invs = object_store.getInvoices { |inv| inv.pkId.in( 1, 2, 3 ) }
|
43
|
+
# # => "select * from invoices where pkId in ( 1, 2, 3 )"
|
44
|
+
#
|
45
|
+
# == Text comparison: +like+
|
46
|
+
# fname_starts_with_a = object_store.getUsers { |user|
|
47
|
+
# user.fname.like( /^a/ )
|
48
|
+
# }
|
49
|
+
# # => "select * from users where fname like 'a%'"
|
50
|
+
# fname_ends_with_a = object_store.getUsers { |user|
|
51
|
+
# user.fname.like( /a$/ )
|
52
|
+
# }
|
53
|
+
# # => "select * from users where fname like '%a'"
|
54
|
+
# fname_contains_a = object_store.getUsers { |user|
|
55
|
+
# user.fname.like( /a/ )
|
56
|
+
# }
|
57
|
+
# # => "select * from users where fname like '%a%'"
|
58
|
+
# Please note that although we're using the Regexp operators here, these aren't
|
59
|
+
# full-fledged regexps. Only ^ and $ work for this.
|
60
|
+
#
|
61
|
+
# == Compound conditions: <tt>Query.And</tt> and <tt>Query.Or</tt>
|
62
|
+
# invoices = object_store.getInvoices { |inv|
|
63
|
+
# Query.And( inv.hours.equals( 40 ), inv.rate.equals( 50 ) )
|
64
|
+
# }
|
65
|
+
# # => "select * from invoices where (hours = 40 and rate = 50)"
|
66
|
+
# client99 = object_store.getClient( 99 )
|
67
|
+
# invoices = object_store.getInvoices { |inv|
|
68
|
+
# Query.Or( inv.hours.equals( 40 ),
|
69
|
+
# inv.rate.equals( 50 ),
|
70
|
+
# inv.client.equals( client99 ) )
|
71
|
+
# }
|
72
|
+
# # => "select * from invoices where (hours = 40 or rate = 50 or client = 99)"
|
73
|
+
# Note that both compound operators can take 2 or more arguments. Also, they can
|
74
|
+
# be nested:
|
75
|
+
# invoices = object_store.getInvoices { |inv|
|
76
|
+
# Query.And( inv.hours.equals( 40 ),
|
77
|
+
# Query.Or( inv.rate.equals( 50 ),
|
78
|
+
# inv.client.equals( client99 ) ) )
|
79
|
+
# }
|
80
|
+
# # => "select * from invoices where (hours = 40 and
|
81
|
+
# # (rate = 50 or client = 99))"
|
82
|
+
#
|
83
|
+
# == Negation: +not+
|
84
|
+
# invoices = object_store.getInvoices { |inv| inv.rate.equals( 50 ).not }
|
85
|
+
# # => "select * from invoices where rate != 50"
|
86
|
+
|
87
|
+
require 'lafcadio/includer'
|
88
|
+
Includer.include( 'query' )
|
89
|
+
|
90
|
+
module Lafcadio
|
91
|
+
class Query
|
92
|
+
def self.And( *conditions ); CompoundCondition.new( *conditions ); end
|
93
|
+
|
94
|
+
def self.Or( *conditions )
|
95
|
+
conditions << CompoundCondition::OR
|
96
|
+
CompoundCondition.new( *conditions)
|
97
|
+
end
|
98
|
+
|
99
|
+
class DomainObjectImpostor #:nodoc:
|
100
|
+
attr_reader :domainClass
|
101
|
+
|
102
|
+
def initialize( domainClass )
|
103
|
+
@domainClass = domainClass
|
104
|
+
end
|
105
|
+
|
106
|
+
def method_missing( methId, *args )
|
107
|
+
fieldName = methId.id2name
|
108
|
+
if fieldName == 'pkId'
|
109
|
+
ObjectFieldImpostor.new( self, fieldName )
|
110
|
+
else
|
111
|
+
begin
|
112
|
+
classField = @domainClass.getField( fieldName )
|
113
|
+
ObjectFieldImpostor.new( self, classField )
|
114
|
+
rescue MissingError
|
115
|
+
super( methId, *args )
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
class Inferrer #:nodoc:
|
122
|
+
def initialize( domainClass, &action )
|
123
|
+
@domainClass = domainClass; @action = action
|
124
|
+
end
|
125
|
+
|
126
|
+
def execute
|
127
|
+
impostor = DomainObjectImpostor.new( @domainClass )
|
128
|
+
condition = @action.call( impostor )
|
129
|
+
query = Query.new( @domainClass, condition )
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class ObjectFieldImpostor #:nodoc:
|
134
|
+
def ObjectFieldImpostor.comparators
|
135
|
+
{
|
136
|
+
'lt' => Compare::LESS_THAN, 'lte' => Compare::LESS_THAN_OR_EQUAL,
|
137
|
+
'gte' => Compare::GREATER_THAN_OR_EQUAL,
|
138
|
+
'gt' => Compare::GREATER_THAN
|
139
|
+
}
|
140
|
+
end
|
141
|
+
|
142
|
+
attr_reader :class_field
|
143
|
+
|
144
|
+
def initialize( domainObjectImpostor, class_field_or_name )
|
145
|
+
@domainObjectImpostor = domainObjectImpostor
|
146
|
+
if class_field_or_name == 'pkId'
|
147
|
+
@db_field_name = 'pkId'
|
148
|
+
else
|
149
|
+
@class_field = class_field_or_name
|
150
|
+
@db_field_name = class_field_or_name.dbFieldName
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def method_missing( methId, *args )
|
155
|
+
methodName = methId.id2name
|
156
|
+
if !ObjectFieldImpostor.comparators.keys.index( methodName ).nil?
|
157
|
+
registerCompareCondition( methodName, *args )
|
158
|
+
else
|
159
|
+
super( methId, *args )
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def registerCompareCondition( compareStr, searchTerm)
|
164
|
+
compareVal = ObjectFieldImpostor.comparators[compareStr]
|
165
|
+
Compare.new( @db_field_name, searchTerm,
|
166
|
+
@domainObjectImpostor.domainClass, compareVal )
|
167
|
+
end
|
168
|
+
|
169
|
+
def equals( searchTerm )
|
170
|
+
Equals.new( @db_field_name, field_or_field_name( searchTerm ),
|
171
|
+
@domainObjectImpostor.domainClass )
|
172
|
+
end
|
173
|
+
|
174
|
+
def field_or_field_name( search_term )
|
175
|
+
if search_term.class == ObjectFieldImpostor
|
176
|
+
search_term.class_field
|
177
|
+
else
|
178
|
+
search_term
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def like( regexp )
|
183
|
+
if regexp.source =~ /^\^(.*)/
|
184
|
+
searchTerm = $1
|
185
|
+
matchType = Query::Like::POST_ONLY
|
186
|
+
elsif regexp.source =~ /(.*)\$$/
|
187
|
+
searchTerm = $1
|
188
|
+
matchType = Query::Like::PRE_ONLY
|
189
|
+
else
|
190
|
+
searchTerm = regexp.source
|
191
|
+
matchType = Query::Like::PRE_AND_POST
|
192
|
+
end
|
193
|
+
Query::Like.new( @db_field_name, searchTerm,
|
194
|
+
@domainObjectImpostor.domainClass, matchType )
|
195
|
+
end
|
196
|
+
|
197
|
+
def in( *searchTerms )
|
198
|
+
Query::In.new( @db_field_name, searchTerms,
|
199
|
+
@domainObjectImpostor.domainClass )
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|