lafcadio 0.7.0 → 0.7.1
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 +1 -1
- data/lib/lafcadio.rb~ +1 -1
- data/lib/lafcadio/domain.rb +3 -3
- data/lib/lafcadio/domain.rb~ +5 -7
- data/lib/lafcadio/objectField.rb +1 -1
- data/lib/lafcadio/objectField.rb~ +561 -0
- data/lib/lafcadio/objectStore.rb +1 -1
- data/lib/lafcadio/objectStore.rb.~1.64.~ +766 -0
- data/lib/lafcadio/objectStore.rb~ +17 -5
- data/lib/lafcadio/query.rb +3 -16
- data/lib/lafcadio/query.rb~ +48 -5
- metadata +4 -2
data/lib/lafcadio.rb
CHANGED
data/lib/lafcadio.rb~
CHANGED
data/lib/lafcadio/domain.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'extensions/module'
|
1
2
|
require 'lafcadio/objectField'
|
2
3
|
require 'lafcadio/util'
|
3
4
|
require 'rexml/document'
|
@@ -359,7 +360,7 @@ module Lafcadio
|
|
359
360
|
if field
|
360
361
|
field
|
361
362
|
else
|
362
|
-
errStr = "Couldn't find field \"#{
|
363
|
+
errStr = "Couldn't find field \"#{ fieldName }\" in " +
|
363
364
|
"#{ self } domain class"
|
364
365
|
raise( MissingError, errStr, caller )
|
365
366
|
end
|
@@ -393,8 +394,7 @@ module Lafcadio
|
|
393
394
|
|
394
395
|
def self.method_missing( methodId, *args ) #:nodoc:
|
395
396
|
method_name = methodId.id2name
|
396
|
-
maybe_field_class_name =
|
397
|
-
'Field'
|
397
|
+
maybe_field_class_name = method_name.underscore_to_camel_case + 'Field'
|
398
398
|
field_class = Lafcadio.const_get( maybe_field_class_name )
|
399
399
|
create_field( field_class, args[0], args[1] || {} )
|
400
400
|
end
|
data/lib/lafcadio/domain.rb~
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'extensions/module'
|
1
2
|
require 'lafcadio/objectField'
|
2
3
|
require 'lafcadio/util'
|
3
4
|
require 'rexml/document'
|
@@ -342,13 +343,10 @@ module Lafcadio
|
|
342
343
|
end
|
343
344
|
|
344
345
|
def self.get_domain_dirs #:nodoc:
|
345
|
-
|
346
|
-
|
347
|
-
domainDirStr = config['domainDirs']
|
348
|
-
if domainDirStr
|
349
|
-
domainDirs = domainDirStr.split(',')
|
346
|
+
if ( domainDirStr = LafcadioConfig.new['domainDirs'] )
|
347
|
+
domainDirStr.split(',')
|
350
348
|
else
|
351
|
-
|
349
|
+
[]
|
352
350
|
end
|
353
351
|
end
|
354
352
|
|
@@ -362,7 +360,7 @@ module Lafcadio
|
|
362
360
|
if field
|
363
361
|
field
|
364
362
|
else
|
365
|
-
errStr = "Couldn't find field \"#{
|
363
|
+
errStr = "Couldn't find field \"#{ fieldName }\" in " +
|
366
364
|
"#{ self } domain class"
|
367
365
|
raise( MissingError, errStr, caller )
|
368
366
|
end
|
data/lib/lafcadio/objectField.rb
CHANGED
@@ -424,7 +424,7 @@ module Lafcadio
|
|
424
424
|
unless name
|
425
425
|
linked_type.name =~ /::/
|
426
426
|
name = $' || linked_type.name
|
427
|
-
name = name.
|
427
|
+
name = name.camel_case_to_underscore
|
428
428
|
end
|
429
429
|
super( domain_class, name )
|
430
430
|
( @linked_type, @delete_cascade ) = linked_type, delete_cascade
|
@@ -0,0 +1,561 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'lafcadio/dateTime'
|
3
|
+
require 'lafcadio/util'
|
4
|
+
|
5
|
+
module Lafcadio
|
6
|
+
# ObjectField is the abstract base class of any field for domain objects.
|
7
|
+
class ObjectField
|
8
|
+
include Comparable
|
9
|
+
|
10
|
+
attr_reader :name, :domain_class
|
11
|
+
attr_accessor :not_null, :db_field_name
|
12
|
+
|
13
|
+
def self.instantiate_from_xml( domain_class, fieldElt ) #:nodoc:
|
14
|
+
parameters = instantiation_parameters( fieldElt )
|
15
|
+
instantiate_with_parameters( domain_class, parameters )
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.instantiate_with_parameters( domain_class, parameters ) #:nodoc:
|
19
|
+
instance = self.new( domain_class, parameters['name'] )
|
20
|
+
if ( db_field_name = parameters['db_field_name'] )
|
21
|
+
instance.db_field_name = db_field_name
|
22
|
+
end
|
23
|
+
instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.instantiation_parameters( fieldElt ) #:nodoc:
|
27
|
+
parameters = {}
|
28
|
+
parameters['name'] = fieldElt.attributes['name']
|
29
|
+
parameters['db_field_name'] = fieldElt.attributes['db_field_name']
|
30
|
+
parameters
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.value_type #:nodoc:
|
34
|
+
Object
|
35
|
+
end
|
36
|
+
|
37
|
+
# [domain_class] The domain class that this object field belongs to.
|
38
|
+
# [name] The name of this field.
|
39
|
+
def initialize( domain_class, name )
|
40
|
+
@domain_class = domain_class
|
41
|
+
@name = name
|
42
|
+
@db_field_name = name
|
43
|
+
@not_null = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def <=>(other)
|
47
|
+
if @domain_class == other.domain_class && name == other.name
|
48
|
+
0
|
49
|
+
else
|
50
|
+
object_id <=> other.object_id
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def bind_write?; false; end #:nodoc:
|
55
|
+
|
56
|
+
def db_table_and_field_name
|
57
|
+
"#{ domain_class.table_name }.#{ db_field_name }"
|
58
|
+
end
|
59
|
+
|
60
|
+
def db_will_automatically_write #:nodoc:
|
61
|
+
false
|
62
|
+
end
|
63
|
+
|
64
|
+
# Returns the name that this field is referenced by in the MySQL table. By
|
65
|
+
# default this is the same as the name; to override it, set
|
66
|
+
# ObjectField#db_field_name.
|
67
|
+
def name_for_sql
|
68
|
+
db_field_name
|
69
|
+
end
|
70
|
+
|
71
|
+
def prev_value(pk_id) #:nodoc:
|
72
|
+
prevObject = ObjectStore.get_object_store.get( @domain_class, pk_id )
|
73
|
+
prevObject.send(name)
|
74
|
+
end
|
75
|
+
|
76
|
+
def process_before_verify(value) #:nodoc:
|
77
|
+
value = @default if value == nil
|
78
|
+
value
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns a string value suitable for committing this field's value to
|
82
|
+
# MySQL.
|
83
|
+
def value_for_sql(value)
|
84
|
+
value || 'null'
|
85
|
+
end
|
86
|
+
|
87
|
+
def verify(value, pk_id) #:nodoc:
|
88
|
+
if value.nil? && not_null
|
89
|
+
raise(
|
90
|
+
FieldValueError,
|
91
|
+
"#{ self.domain_class.name }##{ name } can not be nil.",
|
92
|
+
caller
|
93
|
+
)
|
94
|
+
end
|
95
|
+
verify_non_nil( value, pk_id ) if value
|
96
|
+
end
|
97
|
+
|
98
|
+
def verify_non_nil( value, pk_id )
|
99
|
+
value_type = self.class.value_type
|
100
|
+
unless value.class <= value_type
|
101
|
+
raise(
|
102
|
+
FieldValueError,
|
103
|
+
"#{ domain_class.name }##{ name } needs a " + value_type.name +
|
104
|
+
" value.",
|
105
|
+
caller
|
106
|
+
)
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Given the SQL value string, returns a Ruby-native value.
|
111
|
+
def value_from_sql(string)
|
112
|
+
string
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
# IntegerField represents an integer.
|
117
|
+
class IntegerField < ObjectField
|
118
|
+
def value_from_sql(string) #:nodoc:
|
119
|
+
value = super
|
120
|
+
value ? value.to_i : nil
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# A TextField is expected to contain a string value.
|
125
|
+
class TextField < ObjectField
|
126
|
+
def value_for_sql(value) #:nodoc:
|
127
|
+
if value
|
128
|
+
value = value.gsub(/(\\?')/) { |m| m.length == 1 ? "''" : m }
|
129
|
+
value = value.gsub(/\\/) { '\\\\' }
|
130
|
+
"'#{value}'"
|
131
|
+
else
|
132
|
+
"null"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
# BlobField stores a string value and expects to store its value in a BLOB
|
138
|
+
# field in the database.
|
139
|
+
class BlobField < ObjectField
|
140
|
+
attr_accessor :size
|
141
|
+
|
142
|
+
def self.value_type; String; end
|
143
|
+
|
144
|
+
def bind_write?; true; end #:nodoc:
|
145
|
+
|
146
|
+
def value_for_sql(value); "?"; end #:nodoc:
|
147
|
+
end
|
148
|
+
|
149
|
+
# BooleanField represents a boolean value. By default, it assumes that the
|
150
|
+
# table field represents True and False with the integers 1 and 0. There are
|
151
|
+
# two different ways to change this default.
|
152
|
+
#
|
153
|
+
# First, BooleanField includes a few enumerated defaults. Currently there are
|
154
|
+
# only
|
155
|
+
# * BooleanField::ENUMS_ONE_ZERO (the default, uses integers 1 and 0)
|
156
|
+
# * BooleanField::ENUMS_CAPITAL_YES_NO (uses characters 'Y' and 'N')
|
157
|
+
# In the XML class definition, this field would look like
|
158
|
+
# <field name="field_name" class="BooleanField"
|
159
|
+
# enum_type="ENUMS_CAPITAL_YES_NO"/>
|
160
|
+
# If you're defining a field in Ruby, simply set BooleanField#enum_type to one
|
161
|
+
# of the values.
|
162
|
+
#
|
163
|
+
# For more fine-grained specification you can pass specific values in. Use
|
164
|
+
# this format for the XML class definition:
|
165
|
+
# <field name="field_name" class="BooleanField">
|
166
|
+
# <enums>
|
167
|
+
# <enum key="true">yin</enum>
|
168
|
+
# <enum key="false">tang</enum>
|
169
|
+
# </enums>
|
170
|
+
# </field>
|
171
|
+
# If you're defining the field in Ruby, set BooleanField#enums to a hash.
|
172
|
+
# myBooleanField.enums = { true => 'yin', false => 'yang' }
|
173
|
+
#
|
174
|
+
# +enums+ takes precedence over +enum_type+.
|
175
|
+
class BooleanField < ObjectField
|
176
|
+
ENUMS_ONE_ZERO = 0
|
177
|
+
ENUMS_CAPITAL_YES_NO = 1
|
178
|
+
|
179
|
+
attr_accessor :enum_type, :enums
|
180
|
+
|
181
|
+
def initialize( domain_class, name )
|
182
|
+
super( domain_class, name )
|
183
|
+
@enum_type = ENUMS_ONE_ZERO
|
184
|
+
@enums = nil
|
185
|
+
end
|
186
|
+
|
187
|
+
def false_enum # :nodoc:
|
188
|
+
get_enums[false]
|
189
|
+
end
|
190
|
+
|
191
|
+
def get_enums( value = nil ) # :nodoc:
|
192
|
+
if @enums
|
193
|
+
@enums
|
194
|
+
elsif @enum_type == ENUMS_ONE_ZERO
|
195
|
+
if value.class == String
|
196
|
+
{ true => '1', false => '0' }
|
197
|
+
else
|
198
|
+
{ true => 1, false => 0 }
|
199
|
+
end
|
200
|
+
elsif @enum_type == ENUMS_CAPITAL_YES_NO
|
201
|
+
{ true => 'Y', false => 'N' }
|
202
|
+
else
|
203
|
+
raise MissingError
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def text_enum_type # :nodoc:
|
208
|
+
@enums ? @enums[true].class == String : @enum_type == ENUMS_CAPITAL_YES_NO
|
209
|
+
end
|
210
|
+
|
211
|
+
def true_enum( value = nil ) # :nodoc:
|
212
|
+
get_enums( value )[true]
|
213
|
+
end
|
214
|
+
|
215
|
+
def value_for_sql(value) # :nodoc:
|
216
|
+
if value
|
217
|
+
vfs = true_enum
|
218
|
+
else
|
219
|
+
vfs = false_enum
|
220
|
+
end
|
221
|
+
text_enum_type ? "'#{vfs}'" : vfs
|
222
|
+
end
|
223
|
+
|
224
|
+
def value_from_sql(value, lookupLink = true) # :nodoc:
|
225
|
+
value == true_enum( value )
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
# DateField represents a Date.
|
230
|
+
class DateField < ObjectField
|
231
|
+
def self.value_type # :nodoc:
|
232
|
+
Date
|
233
|
+
end
|
234
|
+
|
235
|
+
def initialize( domain_class, name = "date" )
|
236
|
+
super( domain_class, name )
|
237
|
+
end
|
238
|
+
|
239
|
+
def value_for_sql(value) # :nodoc:
|
240
|
+
value ? "'#{value.to_s}'" : 'null'
|
241
|
+
end
|
242
|
+
|
243
|
+
def value_from_sql(dbiDate, lookupLink = true) # :nodoc:
|
244
|
+
begin
|
245
|
+
dbiDate ? dbiDate.to_date : nil
|
246
|
+
rescue ArgumentError
|
247
|
+
nil
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
# DateTimeField represents a DateTime.
|
253
|
+
class DateTimeField < ObjectField
|
254
|
+
def value_for_sql(value) # :nodoc:
|
255
|
+
if value
|
256
|
+
year = value.year
|
257
|
+
month = value.mon.to_s.pad( 2, "0" )
|
258
|
+
day = value.day.to_s.pad( 2, "0" )
|
259
|
+
hour = value.hour.to_s.pad( 2, "0" )
|
260
|
+
minute = value.min.to_s.pad( 2, "0" )
|
261
|
+
second = value.sec.to_s.pad( 2, "0" )
|
262
|
+
"'#{year}-#{month}-#{day} #{hour}:#{minute}:#{second}'"
|
263
|
+
else
|
264
|
+
"null"
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def value_from_sql(dbi_value, lookupLink = true) # :nodoc:
|
269
|
+
dbi_value ? dbi_value.to_time : nil
|
270
|
+
end
|
271
|
+
end
|
272
|
+
|
273
|
+
# DecimalField represents a decimal value.
|
274
|
+
class DecimalField < ObjectField
|
275
|
+
def self.instantiate_with_parameters( domain_class, parameters ) #:nodoc:
|
276
|
+
self.new( domain_class, parameters['name'] )
|
277
|
+
end
|
278
|
+
|
279
|
+
def self.value_type #:nodoc:
|
280
|
+
Numeric
|
281
|
+
end
|
282
|
+
|
283
|
+
def process_before_verify(value) #:nodoc:
|
284
|
+
value = super value
|
285
|
+
value != nil && value != '' ? value.to_f : nil
|
286
|
+
end
|
287
|
+
|
288
|
+
def value_from_sql(string, lookupLink = true) #:nodoc:
|
289
|
+
string != nil ? string.to_f : nil
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# EmailField takes a text value that is expected to be formatted as a single
|
294
|
+
# valid email address.
|
295
|
+
class EmailField < TextField
|
296
|
+
# Is +address+ a valid email address?
|
297
|
+
def self.valid_address(address)
|
298
|
+
address =~ /^[^ @]+@[^ \.]+\.[^ ,]+$/
|
299
|
+
end
|
300
|
+
|
301
|
+
def initialize( domain_class, name = "email" )
|
302
|
+
super( domain_class, name )
|
303
|
+
end
|
304
|
+
|
305
|
+
def verify_non_nil(value, pk_id) #:nodoc:
|
306
|
+
super(value, pk_id)
|
307
|
+
if !EmailField.valid_address(value)
|
308
|
+
raise(
|
309
|
+
FieldValueError,
|
310
|
+
"#{ domain_class.name }##{ name } needs a valid email address.",
|
311
|
+
caller
|
312
|
+
)
|
313
|
+
end
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
# EnumField represents an enumerated field that can only be set to one of a
|
318
|
+
# set range of string values. To set the enumeration in the class definition
|
319
|
+
# XML, use the following format:
|
320
|
+
# <field name="flavor" class="EnumField">
|
321
|
+
# <enums>
|
322
|
+
# <enum>Vanilla</enum>
|
323
|
+
# <enum>Chocolate</enum>
|
324
|
+
# <enum>Lychee</enum>
|
325
|
+
# </enums>
|
326
|
+
# </field>
|
327
|
+
# If you're defining the field in Ruby, you can simply pass in an array of
|
328
|
+
# enums as the +enums+ argument.
|
329
|
+
#
|
330
|
+
class EnumField < TextField
|
331
|
+
def self.instantiate_with_parameters( domain_class, parameters ) #:nodoc:
|
332
|
+
self.new( domain_class, parameters['name'], parameters['enums'] )
|
333
|
+
end
|
334
|
+
|
335
|
+
def self.enum_queue_hash( fieldElt )
|
336
|
+
enumValues = []
|
337
|
+
fieldElt.elements.each( 'enums/enum' ) { |enumElt|
|
338
|
+
enumValues << enumElt.attributes['key']
|
339
|
+
enumValues << enumElt.text.to_s
|
340
|
+
}
|
341
|
+
QueueHash.new( *enumValues )
|
342
|
+
end
|
343
|
+
|
344
|
+
def self.instantiation_parameters( fieldElt ) #:nodoc:
|
345
|
+
parameters = super( fieldElt )
|
346
|
+
if fieldElt.elements['enums'][1].attributes['key']
|
347
|
+
parameters['enums'] = enum_queue_hash( fieldElt )
|
348
|
+
else
|
349
|
+
parameters['enums'] = []
|
350
|
+
fieldElt.elements.each( 'enums/enum' ) { |enumElt|
|
351
|
+
parameters['enums'] << enumElt.text.to_s
|
352
|
+
}
|
353
|
+
end
|
354
|
+
parameters
|
355
|
+
end
|
356
|
+
|
357
|
+
attr_reader :enums
|
358
|
+
|
359
|
+
# [domain_class] The domain class that this field belongs to.
|
360
|
+
# [name] The name of this domain class.
|
361
|
+
# [enums] An array of Strings representing the possible choices for
|
362
|
+
# this field.
|
363
|
+
def initialize( domain_class, name, enums )
|
364
|
+
super( domain_class, name )
|
365
|
+
if enums.class == Array
|
366
|
+
@enums = QueueHash.new_from_array enums
|
367
|
+
else
|
368
|
+
@enums = enums
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def value_for_sql(value) #:nodoc:
|
373
|
+
value != '' ?(super(value)) : 'null'
|
374
|
+
end
|
375
|
+
|
376
|
+
def verify_non_nil( value, pk_id ) #:nodoc:
|
377
|
+
super
|
378
|
+
if @enums[value].nil?
|
379
|
+
key_str = '[ ' +
|
380
|
+
( @enums.keys.map { |key| "\"#{ key }\"" } ).join(', ') + ' ]'
|
381
|
+
err_str = "#{ @domain_class.name }##{ name } needs a value that is " +
|
382
|
+
"one of #{ key_str }"
|
383
|
+
raise( FieldValueError, err_str, caller )
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
|
388
|
+
class FieldValueError < RuntimeError #:nodoc:
|
389
|
+
end
|
390
|
+
|
391
|
+
# A LinkField is used to link from one domain class to another.
|
392
|
+
class LinkField < ObjectField
|
393
|
+
def self.instantiate_with_parameters( domain_class, parameters ) #:nodoc:
|
394
|
+
instance = self.new(
|
395
|
+
domain_class, parameters['linked_type'], parameters['name'],
|
396
|
+
parameters['delete_cascade']
|
397
|
+
)
|
398
|
+
if parameters['db_field_name']
|
399
|
+
instance.db_field_name = parameters['db_field_name']
|
400
|
+
end
|
401
|
+
instance
|
402
|
+
end
|
403
|
+
|
404
|
+
def self.instantiation_parameters( fieldElt ) #:nodoc:
|
405
|
+
parameters = super( fieldElt )
|
406
|
+
linked_typeStr = fieldElt.attributes['linked_type']
|
407
|
+
linked_type = DomainObject.get_domain_class_from_string( linked_typeStr )
|
408
|
+
parameters['linked_type'] = linked_type
|
409
|
+
parameters['delete_cascade'] = fieldElt.attributes['delete_cascade'] == 'y'
|
410
|
+
parameters
|
411
|
+
end
|
412
|
+
|
413
|
+
attr_reader :linked_type
|
414
|
+
attr_accessor :delete_cascade
|
415
|
+
|
416
|
+
# [domain_class] The domain class that this field belongs to.
|
417
|
+
# [linked_type] The domain class that this field points to.
|
418
|
+
# [name] The name of this field.
|
419
|
+
# [delete_cascade] If this is true, deleting the domain object that is
|
420
|
+
# linked to will cause this domain object to be deleted
|
421
|
+
# as well.
|
422
|
+
def initialize( domain_class, linked_type, name = nil,
|
423
|
+
delete_cascade = false )
|
424
|
+
unless name
|
425
|
+
linked_type.name =~ /::/
|
426
|
+
name = $' || linked_type.name
|
427
|
+
name = name.decapitalize
|
428
|
+
end
|
429
|
+
super( domain_class, name )
|
430
|
+
( @linked_type, @delete_cascade ) = linked_type, delete_cascade
|
431
|
+
end
|
432
|
+
|
433
|
+
def value_from_sql(string) #:nodoc:
|
434
|
+
string != nil ? DomainObjectProxy.new(@linked_type, string.to_i) : nil
|
435
|
+
end
|
436
|
+
|
437
|
+
def value_for_sql(value) #:nodoc:
|
438
|
+
if !value
|
439
|
+
"null"
|
440
|
+
elsif value.pk_id
|
441
|
+
value.pk_id
|
442
|
+
else
|
443
|
+
raise( DomainObjectInitError, "Can't commit #{name} without pk_id",
|
444
|
+
caller )
|
445
|
+
end
|
446
|
+
end
|
447
|
+
|
448
|
+
def verify_non_nil(value, pk_id) #:nodoc:
|
449
|
+
super
|
450
|
+
if @linked_type != @domain_class && pk_id
|
451
|
+
subsetLinkField = @linked_type.class_fields.find { |field|
|
452
|
+
field.class == SubsetLinkField && field.subset_field == @name
|
453
|
+
}
|
454
|
+
if subsetLinkField
|
455
|
+
verify_subset_link_field( subsetLinkField, pk_id )
|
456
|
+
end
|
457
|
+
end
|
458
|
+
end
|
459
|
+
|
460
|
+
def verify_subset_link_field( subsetLinkField, pk_id )
|
461
|
+
begin
|
462
|
+
prevObj = ObjectStore.get_object_store.get( domain_class, pk_id )
|
463
|
+
prevObjLinkedTo = prevObj.send(name)
|
464
|
+
possiblyMyObj = prevObjLinkedTo.send(subsetLinkField.name)
|
465
|
+
if possiblyMyObj && possiblyMyObj.pk_id == pk_id
|
466
|
+
cantChangeMsg = "You can't change that."
|
467
|
+
raise FieldValueError, cantChangeMsg, caller
|
468
|
+
end
|
469
|
+
rescue DomainObjectNotFoundError
|
470
|
+
# no previous value, so nothing to check for
|
471
|
+
end
|
472
|
+
end
|
473
|
+
end
|
474
|
+
|
475
|
+
# Accepts a Month as a value. This field automatically saves in MySQL as a
|
476
|
+
# date corresponding to the first day of the month.
|
477
|
+
class MonthField < DateField
|
478
|
+
def self.value_type #:nodoc:
|
479
|
+
Month
|
480
|
+
end
|
481
|
+
|
482
|
+
def value_for_sql(value) #:nodoc:
|
483
|
+
"'#{value.year}-#{value.month}-01'"
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
class PrimaryKeyField < IntegerField
|
488
|
+
def initialize( domain_class )
|
489
|
+
super( domain_class, 'pk_id' )
|
490
|
+
@not_null = false
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
# A StateField is a specialized subclass of EnumField; its possible values are
|
495
|
+
# any of the 50 states of the United States, stored as each state's two-letter
|
496
|
+
# postal code.
|
497
|
+
class StateField < EnumField
|
498
|
+
def initialize( domain_class, name = "state" )
|
499
|
+
super( domain_class, name, UsStates.states )
|
500
|
+
end
|
501
|
+
end
|
502
|
+
|
503
|
+
class SubsetLinkField < LinkField #:nodoc:
|
504
|
+
def self.instantiate_with_parameters( domain_class, parameters )
|
505
|
+
self.new( domain_class, parameters['linked_type'],
|
506
|
+
parameters['subset_field'], parameters['name'] )
|
507
|
+
end
|
508
|
+
|
509
|
+
def self.instantiation_parameters( fieldElt )
|
510
|
+
parameters = super( fieldElt )
|
511
|
+
parameters['subset_field'] = fieldElt.attributes['subset_field']
|
512
|
+
parameters
|
513
|
+
end
|
514
|
+
|
515
|
+
attr_accessor :subset_field
|
516
|
+
|
517
|
+
def initialize( domain_class, linked_type, subset_field,
|
518
|
+
name = linked_type.name.downcase )
|
519
|
+
super( domain_class, linked_type, name )
|
520
|
+
@subset_field = subset_field
|
521
|
+
end
|
522
|
+
end
|
523
|
+
|
524
|
+
# TextListField maps to any String SQL field that tries to represent a
|
525
|
+
# quick-and-dirty list with a comma-separated string. It returns an Array.
|
526
|
+
# For example, a SQL field with the value "john,bill,dave", then the Ruby
|
527
|
+
# field will have the value <tt>[ "john", "bill", "dave" ]</tt>.
|
528
|
+
class TextListField < ObjectField
|
529
|
+
def self.value_type #:nodoc:
|
530
|
+
Array
|
531
|
+
end
|
532
|
+
|
533
|
+
def value_for_sql(objectValue) #:nodoc:
|
534
|
+
if objectValue.is_a?( Array )
|
535
|
+
str = objectValue.join(',')
|
536
|
+
else
|
537
|
+
str = objectValue
|
538
|
+
end
|
539
|
+
"'" + str + "'"
|
540
|
+
end
|
541
|
+
|
542
|
+
def value_from_sql(sqlString, lookupLink = true) #:nodoc:
|
543
|
+
if sqlString
|
544
|
+
sqlString.split ','
|
545
|
+
else
|
546
|
+
[]
|
547
|
+
end
|
548
|
+
end
|
549
|
+
end
|
550
|
+
|
551
|
+
class TimeStampField < DateTimeField #:nodoc:
|
552
|
+
def initialize( domain_class, name = 'timeStamp' )
|
553
|
+
super( domain_class, name )
|
554
|
+
@not_null = false
|
555
|
+
end
|
556
|
+
|
557
|
+
def db_will_automatically_write
|
558
|
+
true
|
559
|
+
end
|
560
|
+
end
|
561
|
+
end
|