lore 0.4.2
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/LICENSE +19 -0
- data/README +74 -0
- data/aspect.rb +80 -0
- data/behaviours/lockable.rb +41 -0
- data/behaviours/movable.rb +54 -0
- data/behaviours/versioned.rb +24 -0
- data/benchmark.rb +193 -0
- data/bits.rb +52 -0
- data/cache/abstract_entity_cache.rb +82 -0
- data/cache/bits.rb +22 -0
- data/cache/cacheable.rb +202 -0
- data/cache/cached_entities.rb +116 -0
- data/cache/file_index.rb +35 -0
- data/cache/mmap_entity_cache.rb +67 -0
- data/clause.rb +528 -0
- data/connection.rb +155 -0
- data/custom_functions.sql +14 -0
- data/exception/ambiguous_attribute.rb +14 -0
- data/exception/cache_exception.rb +30 -0
- data/exception/invalid_klass_parameters.rb +63 -0
- data/exception/invalid_parameter.rb +42 -0
- data/exception/unknown_typecode.rb +19 -0
- data/file_index.sql +56 -0
- data/gui/erb_template.rb +79 -0
- data/gui/erb_template_helpers.rhtml +19 -0
- data/gui/form.rb +314 -0
- data/gui/form_element.rb +676 -0
- data/gui/form_generator.rb +151 -0
- data/gui/templates/button.rhtml +2 -0
- data/gui/templates/checkbox.rhtml +3 -0
- data/gui/templates/checkbox_row.rhtml +1 -0
- data/gui/templates/file.rhtml +2 -0
- data/gui/templates/file_readonly.rhtml +3 -0
- data/gui/templates/form_element.rhtml +5 -0
- data/gui/templates/form_element_horizontal.rhtml +3 -0
- data/gui/templates/form_element_listed.rhtml +8 -0
- data/gui/templates/form_table.rhtml +3 -0
- data/gui/templates/form_table_blank.rhtml +3 -0
- data/gui/templates/form_table_horizontal.rhtml +8 -0
- data/gui/templates/password.rhtml +2 -0
- data/gui/templates/password_readonly.rhtml +3 -0
- data/gui/templates/radio.rhtml +1 -0
- data/gui/templates/radio_row.rhtml +1 -0
- data/gui/templates/select.rhtml +23 -0
- data/gui/templates/text.rhtml +2 -0
- data/gui/templates/text_readonly.rhtml +3 -0
- data/gui/templates/textarea.rhtml +3 -0
- data/gui/templates/textarea_readonly.rhtml +4 -0
- data/lore.gemspec +40 -0
- data/lore.rb +94 -0
- data/migration.rb +48 -0
- data/model.rb +139 -0
- data/model_factory.rb +202 -0
- data/model_shortcuts.rb +16 -0
- data/query_shortcuts.rb +367 -0
- data/reserved_methods.txt +3 -0
- data/result.rb +100 -0
- data/symbol.rb +58 -0
- data/table_accessor.rb +1926 -0
- data/table_deleter.rb +115 -0
- data/table_inserter.rb +168 -0
- data/table_instance.rb +384 -0
- data/table_selector.rb +314 -0
- data/table_updater.rb +155 -0
- data/test/README +31 -0
- data/test/env.rb +5 -0
- data/test/lore_test.log +8218 -0
- data/test/model.rb +142 -0
- data/test/prepare.rb +37 -0
- data/test/tc_aspect.rb +58 -0
- data/test/tc_cache.rb +80 -0
- data/test/tc_clause.rb +104 -0
- data/test/tc_deep_inheritance.rb +49 -0
- data/test/tc_factory.rb +57 -0
- data/test/tc_filter.rb +37 -0
- data/test/tc_form.rb +32 -0
- data/test/tc_model.rb +86 -0
- data/test/tc_prepare.rb +45 -0
- data/test/tc_refined_query.rb +88 -0
- data/test/tc_table_accessor.rb +265 -0
- data/test/test.log +181 -0
- data/test/test_db.sql +400 -0
- data/test/ts_lore.rb +49 -0
- data/types.rb +55 -0
- data/validation/message.rb +60 -0
- data/validation/parameter_validator.rb +104 -0
- data/validation/reason.rb +54 -0
- data/validation/type_validator.rb +91 -0
- data/validation.rb +65 -0
- metadata +170 -0
data/table_accessor.rb
ADDED
@@ -0,0 +1,1926 @@
|
|
1
|
+
|
2
|
+
require('logger')
|
3
|
+
require('lore/types')
|
4
|
+
require('lore/clause')
|
5
|
+
require('lore/aspect')
|
6
|
+
require('lore/table_instance')
|
7
|
+
require('lore/table_selector')
|
8
|
+
require('lore/table_inserter')
|
9
|
+
require('lore/table_updater')
|
10
|
+
require('lore/table_deleter')
|
11
|
+
require('lore/query_shortcuts')
|
12
|
+
require('lore/model_shortcuts')
|
13
|
+
|
14
|
+
module Lore
|
15
|
+
|
16
|
+
class Table_Accessor
|
17
|
+
include Table_Instance
|
18
|
+
extend Aspect
|
19
|
+
extend Query_Shortcuts
|
20
|
+
extend Model_Shortcuts
|
21
|
+
|
22
|
+
attr_reader :constraints, :foreign_keys, :primary_keys, :attributes, :attribute_types
|
23
|
+
|
24
|
+
@@logger = Logger.new(Lore.logfile)
|
25
|
+
|
26
|
+
def self.log(message, level=:debug)
|
27
|
+
@@logger.debug(message)
|
28
|
+
end
|
29
|
+
def log(message, level=:debug)
|
30
|
+
@@logger.debug(message)
|
31
|
+
end
|
32
|
+
|
33
|
+
# tell dispatchers to validate Table_Accessors
|
34
|
+
def self.validate_params?() true end
|
35
|
+
|
36
|
+
# {{{ FIELDS
|
37
|
+
# foreign_keys is a hash that looks like:
|
38
|
+
# "foreign_table" => array ("foreign_key1", "foreign_key_2" ... )
|
39
|
+
@foreign_keys = Hash.new
|
40
|
+
|
41
|
+
# primary_keys is a hash holding the own foreign key fields,
|
42
|
+
# and the parents ones:
|
43
|
+
# "parent_table" => array ("prim_key_1", "prim_key_2", ... )
|
44
|
+
# "own_table" => array ("prim_key_1", "prim_key_2", ... )
|
45
|
+
@primary_keys = Hash.new
|
46
|
+
|
47
|
+
# implicit|explicit_attributes = Hash {'table_a' =>
|
48
|
+
# { 'attrib_a1', attrib_a2, ... },
|
49
|
+
# 'table_b' =>
|
50
|
+
# { 'attrib_b1', attrib_b2, ... },
|
51
|
+
# ...}
|
52
|
+
# implicit_attributes stores attributes that will be resolved implicitly
|
53
|
+
# and do not have to be passed on self.create
|
54
|
+
@implicit_attributes = Hash.new
|
55
|
+
# explicit_attributes stores attributes that have to be passed
|
56
|
+
# on self.create and will not be resolved implicitly
|
57
|
+
@explicit_attributes = Hash.new
|
58
|
+
|
59
|
+
# Array storing sequences for primary keys. If no sequence is set,
|
60
|
+
# the value for this primary key has to be resolved from parent,
|
61
|
+
# or passed directly.
|
62
|
+
@sequences = Hash.new
|
63
|
+
|
64
|
+
# Hash holding own table attribute fields as string
|
65
|
+
@attributes = nil
|
66
|
+
|
67
|
+
# Like @attributes, but listing fields as array
|
68
|
+
# Example:
|
69
|
+
# [ 'public.the_table.foo', 'public.other_table.bar' ]
|
70
|
+
@attribute_array = Array.new
|
71
|
+
|
72
|
+
# array holding own table attribute types as { 'attrib_name' => int type}
|
73
|
+
@attribute_types = Hash.new
|
74
|
+
|
75
|
+
# Holds own table name, defaults to self.class if not given.
|
76
|
+
# If a schema has been set by
|
77
|
+
# table :table_name, :schema_name
|
78
|
+
# table_name is schema_name.table_name
|
79
|
+
@table_name = String.new
|
80
|
+
|
81
|
+
# has_a, has_n and is_a hold all table names they are referring to:
|
82
|
+
# array ("foreign_table_1", "foreign_table_2", ... )
|
83
|
+
# The respective foreign keys reside in foreign_keys:
|
84
|
+
# foreign_keys["foreign_table_1"] -> array ("foreign_key_1", ... )
|
85
|
+
@has_a = Array.new
|
86
|
+
@has_n = Array.new
|
87
|
+
|
88
|
+
# Hash is_a holds the relational derivation tree of this class.
|
89
|
+
@is_a = Hash.new
|
90
|
+
|
91
|
+
# Hash is_a_klasses holds the relational derivation tree of this class.
|
92
|
+
@is_a_klasses = Hash.new
|
93
|
+
@has_a_klasses = Hash.new
|
94
|
+
|
95
|
+
@constraints = Hash.new
|
96
|
+
|
97
|
+
# Hash explicit holds table fields that are not to be updated
|
98
|
+
# implicitly, e.g. timestamps.
|
99
|
+
@explicit = Hash.new
|
100
|
+
|
101
|
+
@labels = Array.new
|
102
|
+
#}}} END FIELDS
|
103
|
+
|
104
|
+
@input_filters = Hash.new
|
105
|
+
@output_filters = Hash.new
|
106
|
+
|
107
|
+
@@prepared_statements = Hash.new
|
108
|
+
|
109
|
+
@@cache_impl = false
|
110
|
+
|
111
|
+
def self.entity_cache
|
112
|
+
@@cache_impl
|
113
|
+
end
|
114
|
+
|
115
|
+
def self.use_entity_cache(entity_cache_class)
|
116
|
+
log('Setting cache implementation: ' << entity_cache_class.inspect)
|
117
|
+
@@cache_impl = entity_cache_class
|
118
|
+
end
|
119
|
+
|
120
|
+
def self.flush_entity_cache
|
121
|
+
@@cache_impl.flush(self) if @@cache_impl
|
122
|
+
end
|
123
|
+
|
124
|
+
def self.create_entity_cache(query_string, result)
|
125
|
+
@@cache_impl.create(self, query_string, result) if @@cache_impl
|
126
|
+
end
|
127
|
+
|
128
|
+
def self.read_entity_cache(query_string)
|
129
|
+
@@cache_impl.read(self, query_string) if @@cache_impl
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
##########################################################################
|
134
|
+
# Recursively gets foreign keys from parent, if own
|
135
|
+
# foreign keys don't have been set, yet:
|
136
|
+
def self.get_foreign_keys
|
137
|
+
# {{{
|
138
|
+
|
139
|
+
if @foreign_keys.nil? then
|
140
|
+
if @foreign_keys_klasses.nil? then
|
141
|
+
@foreign_keys = Hash.new
|
142
|
+
else
|
143
|
+
@is_a_klasses.each { |foreign_key, k|
|
144
|
+
@foreign_keys[k.table_name] << k.get_foreign_keys
|
145
|
+
}
|
146
|
+
end
|
147
|
+
else
|
148
|
+
return @foreign_keys
|
149
|
+
end
|
150
|
+
|
151
|
+
end # }}}
|
152
|
+
def self.set_foreign_keys(arg)
|
153
|
+
# {{{
|
154
|
+
@foreign_keys = arg
|
155
|
+
end # }}}
|
156
|
+
|
157
|
+
##########################################################################
|
158
|
+
# Recursively gets primary keys from parent, if own
|
159
|
+
# primary keys don't have been set, yet:
|
160
|
+
# Returns all derived primary keys WITHOUT OWN PKEYS.
|
161
|
+
def self.get_primary_keys
|
162
|
+
# {{{
|
163
|
+
|
164
|
+
if @primary_keys.nil? then
|
165
|
+
if @is_a_klasses.nil? then
|
166
|
+
return Hash.new
|
167
|
+
else
|
168
|
+
plain_base_pkeys = Hash.new
|
169
|
+
formatted_base_pkeys = Hash.new
|
170
|
+
base_schema = ''
|
171
|
+
|
172
|
+
get_is_a_klasses.each { |k|
|
173
|
+
plain_base_pkeys = k.get_primary_keys
|
174
|
+
base_schema = k.get_schema
|
175
|
+
if base_schema.nil? then
|
176
|
+
formatted_base_pkeys.update(plain_base_pkeys)
|
177
|
+
else
|
178
|
+
plain_base_pkeys.each_pair { |key, value|
|
179
|
+
formatted_base_pkeys[base_schema+'.'+key] = value
|
180
|
+
}
|
181
|
+
return formatted_base_pkeys
|
182
|
+
@primary_keys = formatted_base_pkeys
|
183
|
+
end
|
184
|
+
}
|
185
|
+
return formatted_base_pkeys
|
186
|
+
@primary_keys = formatted_base_pkeys
|
187
|
+
end
|
188
|
+
else
|
189
|
+
return @primary_keys
|
190
|
+
end
|
191
|
+
|
192
|
+
end # }}}
|
193
|
+
|
194
|
+
def self.key_array()
|
195
|
+
# {{{
|
196
|
+
keys = Array.new
|
197
|
+
get_primary_keys.each_pair { |table, attribs|
|
198
|
+
attribs.each { |attrib|
|
199
|
+
keys.push attrib
|
200
|
+
}
|
201
|
+
}
|
202
|
+
return keys
|
203
|
+
end # }}}
|
204
|
+
|
205
|
+
##########################################################################
|
206
|
+
# Recursively gets sequences from parent, if own
|
207
|
+
# sequences don't have been set, yet:
|
208
|
+
def self.get_sequences
|
209
|
+
# {{{
|
210
|
+
|
211
|
+
if @sequences.nil? then
|
212
|
+
if @is_a_klasses.nil? then
|
213
|
+
return Hash.new
|
214
|
+
else
|
215
|
+
seq_map = Hash.new
|
216
|
+
@is_a_klasses.each_pair { |foreign_key,k|
|
217
|
+
seq_map[k.table_name] = k.get_sequences
|
218
|
+
}
|
219
|
+
return seq_map
|
220
|
+
end
|
221
|
+
else
|
222
|
+
return @sequences
|
223
|
+
end
|
224
|
+
|
225
|
+
end # }}}
|
226
|
+
|
227
|
+
def self.set_sequences(arg) # :nodoc:
|
228
|
+
# {{{
|
229
|
+
@sequences = arg
|
230
|
+
end # }}}
|
231
|
+
|
232
|
+
##########################################################################
|
233
|
+
# @implicit_attributes getters/setters {{{
|
234
|
+
|
235
|
+
def self.set_implicit_attributes(arg) # :nodoc:
|
236
|
+
@implicit_attributes = arg
|
237
|
+
end # def
|
238
|
+
def self.get_implicit_attributes # :nodoc:
|
239
|
+
if @implicit_attributes.nil? then
|
240
|
+
return Hash.new
|
241
|
+
else
|
242
|
+
return @implicit_attributes
|
243
|
+
end
|
244
|
+
end # def
|
245
|
+
# }}}
|
246
|
+
|
247
|
+
##########################################################################
|
248
|
+
# @explicit_attributes getters/setters {{{
|
249
|
+
|
250
|
+
def self.set_explicit_attributes(arg) # :nodoc:
|
251
|
+
@explicit_attributes = arg
|
252
|
+
end # def
|
253
|
+
def self.get_explicit_attributes # :nodoc:
|
254
|
+
|
255
|
+
if @explicit_attributes.nil? then
|
256
|
+
return Hash.new
|
257
|
+
else
|
258
|
+
# filter entries that are present in @implicit_attributes:
|
259
|
+
if !@implicit_attributes.nil? then
|
260
|
+
|
261
|
+
@explicit_attributes.each_pair { |table, attribs|
|
262
|
+
attribs.delete_if { |attrib|
|
263
|
+
!@implicit_attributes[table].nil? &&
|
264
|
+
@implicit_attributes[table].include?(attrib.to_s)
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
end
|
269
|
+
|
270
|
+
return @explicit_attributes
|
271
|
+
|
272
|
+
end
|
273
|
+
end # def
|
274
|
+
# }}}
|
275
|
+
|
276
|
+
def self.get_constraints
|
277
|
+
|
278
|
+
@constraints = Hash.new if @constraints.nil?
|
279
|
+
if !@is_a_klasses.nil? then
|
280
|
+
@is_a_klasses.each_pair { |foreign_key, klass|
|
281
|
+
@constraints.update(klass.get_constraints)
|
282
|
+
}
|
283
|
+
end
|
284
|
+
@constraints
|
285
|
+
|
286
|
+
end
|
287
|
+
|
288
|
+
##########################################################################
|
289
|
+
# @table getters/setters {{{
|
290
|
+
|
291
|
+
def self.get_table_name # :nodoc:
|
292
|
+
return @table_name unless @table_name.nil?
|
293
|
+
return ''
|
294
|
+
end
|
295
|
+
def self.table_name # :nodoc:
|
296
|
+
get_table_name
|
297
|
+
end
|
298
|
+
def self.set_table_name(arg) # :nodoc:
|
299
|
+
@table_name = arg
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.get_table_names
|
303
|
+
return @table_names if @table_names
|
304
|
+
@table_names = [@table_name]
|
305
|
+
@table_names += get_is_a.keys_flat
|
306
|
+
return @table_names
|
307
|
+
end
|
308
|
+
def self.get_all_table_names
|
309
|
+
return @table_names if @table_names
|
310
|
+
@table_names = [@table_name]
|
311
|
+
@table_names += get_joins.keys_flat
|
312
|
+
return @table_names
|
313
|
+
end
|
314
|
+
# }}}
|
315
|
+
|
316
|
+
|
317
|
+
protected
|
318
|
+
|
319
|
+
##########################################################################
|
320
|
+
# label setters/getters {{{
|
321
|
+
|
322
|
+
# Define attribute to use as label for instances of a Table_Accessor,
|
323
|
+
# e.g. for select boxes.
|
324
|
+
def self.use_label(*attribs)
|
325
|
+
|
326
|
+
@labels = attribs.map { |e|
|
327
|
+
|
328
|
+
e = e.to_s # also removes Array wraps like [:attrib_name]
|
329
|
+
|
330
|
+
if((e.kind_of? Clause) or (e.include?('.'))) then
|
331
|
+
e = e.to_s
|
332
|
+
else
|
333
|
+
e = @table_name + '.' << e.to_s
|
334
|
+
end
|
335
|
+
}
|
336
|
+
@label = attribs[0]
|
337
|
+
end
|
338
|
+
# Returns full name of attribute set to use as label e.g. for
|
339
|
+
# select boxes.
|
340
|
+
def self.get_label
|
341
|
+
@labels[0]
|
342
|
+
end
|
343
|
+
|
344
|
+
def self.get_labels
|
345
|
+
@labels
|
346
|
+
end
|
347
|
+
|
348
|
+
# }}}
|
349
|
+
|
350
|
+
##########################################################################
|
351
|
+
# @attributes getters/setters {{{
|
352
|
+
|
353
|
+
def self.get_attributes # :nodoc:
|
354
|
+
return @attributes unless @attributes.nil?
|
355
|
+
return Hash.new
|
356
|
+
end
|
357
|
+
|
358
|
+
def self.get_attribute_array
|
359
|
+
@attribute_array
|
360
|
+
end
|
361
|
+
|
362
|
+
def self.set_attributes(arg) # :nodoc:
|
363
|
+
@attribute_array = []
|
364
|
+
@attributes = arg
|
365
|
+
@attributes.each_pair { |table, attribs|
|
366
|
+
attribs.each { |a|
|
367
|
+
@attribute_array << "#{table}.#{a}"
|
368
|
+
@attribute_array << a
|
369
|
+
}
|
370
|
+
}
|
371
|
+
end
|
372
|
+
# }}}
|
373
|
+
|
374
|
+
def self.get_input_filters
|
375
|
+
@input_filters
|
376
|
+
end
|
377
|
+
def self.get_output_filters
|
378
|
+
@output_filters
|
379
|
+
end
|
380
|
+
|
381
|
+
##########################################################################
|
382
|
+
# @attribute_types getters/setters {{{
|
383
|
+
|
384
|
+
def self.get_attribute_types # :nodoc:
|
385
|
+
(@attribute_types.nil?) ? Hash.new : @attribute_types
|
386
|
+
end
|
387
|
+
def self.set_attribute_types(arg) # :nodoc:
|
388
|
+
@attribute_types = arg
|
389
|
+
end
|
390
|
+
def self.attribute_type(attrib_args={})
|
391
|
+
attrib_args[:table] = table_name unless attrib_args[:table]
|
392
|
+
@attribute_types[attrib_args[:table]][attrib_args[:attribute]]
|
393
|
+
end
|
394
|
+
# }}}
|
395
|
+
|
396
|
+
|
397
|
+
##########################################################################
|
398
|
+
# @has_a getters/setters {{{
|
399
|
+
def self.get_has_a # :nodoc:
|
400
|
+
|
401
|
+
if @has_a.nil? then
|
402
|
+
if @has_a_klasses.nil? then
|
403
|
+
@has_a = Array.new
|
404
|
+
else
|
405
|
+
@has_a_klasses.each { |k|
|
406
|
+
@has_a << k
|
407
|
+
}
|
408
|
+
end
|
409
|
+
else
|
410
|
+
return @has_a
|
411
|
+
end
|
412
|
+
|
413
|
+
end
|
414
|
+
def self.set_has_a(arg) # :nodoc:
|
415
|
+
@has_a = arg
|
416
|
+
end
|
417
|
+
# }}}
|
418
|
+
|
419
|
+
##########################################################################
|
420
|
+
# @has_a getters/setters {{{
|
421
|
+
|
422
|
+
def self.get_has_n # :nodoc:
|
423
|
+
|
424
|
+
if @has_n.nil? then
|
425
|
+
if @has_n_klasses.nil? then
|
426
|
+
@has_n = Array.new
|
427
|
+
else
|
428
|
+
@has_n_klasses.each { |k|
|
429
|
+
@has_n << k
|
430
|
+
}
|
431
|
+
end
|
432
|
+
else
|
433
|
+
return @has_n
|
434
|
+
end
|
435
|
+
|
436
|
+
end
|
437
|
+
def self.set_has_n(arg) # :nodoc:
|
438
|
+
@has_n = arg
|
439
|
+
end
|
440
|
+
# }}}
|
441
|
+
|
442
|
+
##########################################################################
|
443
|
+
# @is_a getters/setters {{{
|
444
|
+
# don't collect is_a from ancestors!
|
445
|
+
|
446
|
+
def self.get_is_a # :nodoc:
|
447
|
+
if @is_a.nil?
|
448
|
+
Hash.new
|
449
|
+
else
|
450
|
+
@is_a
|
451
|
+
end
|
452
|
+
end
|
453
|
+
def self.set_is_a(arg) # :nodoc:
|
454
|
+
@is_a = arg
|
455
|
+
end
|
456
|
+
# }}}
|
457
|
+
|
458
|
+
##########################################################################
|
459
|
+
# @aggregates getters/setters {{{
|
460
|
+
# don't collect aggregates from ancestors!
|
461
|
+
|
462
|
+
def self.get_aggregates # "nodoc:
|
463
|
+
if @aggregates.nil?
|
464
|
+
Hash.new
|
465
|
+
else
|
466
|
+
@aggregates
|
467
|
+
end
|
468
|
+
end
|
469
|
+
def self.set_aggregates(arg) # :nodoc:
|
470
|
+
@aggregates = arg
|
471
|
+
end
|
472
|
+
# }}}
|
473
|
+
|
474
|
+
def self.get_joins
|
475
|
+
# {{{
|
476
|
+
@joins = get_is_a.dup.update(get_aggregates) if @joins.nil?
|
477
|
+
return @joins
|
478
|
+
end # def }}}
|
479
|
+
|
480
|
+
##########################################################################
|
481
|
+
# @is_a_klasses getters/setters {{{
|
482
|
+
# don't collect is_a from ancestors!
|
483
|
+
|
484
|
+
def self.get_is_a_klasses # :nodoc:
|
485
|
+
if @is_a_klasses.nil?
|
486
|
+
Hash.new
|
487
|
+
else
|
488
|
+
@is_a_klasses
|
489
|
+
end
|
490
|
+
end
|
491
|
+
def self.set_is_a_klasses(arg) # :nodoc:
|
492
|
+
@is_a_klasses = arg
|
493
|
+
end
|
494
|
+
# }}}
|
495
|
+
|
496
|
+
##########################################################################
|
497
|
+
# @aggregate_klasses getters/setters {{{
|
498
|
+
# don't collect is_a from ancestors!
|
499
|
+
|
500
|
+
def self.get_aggregate_klasses # :nodoc:
|
501
|
+
if @aggregate_klasses.nil?
|
502
|
+
Hash.new
|
503
|
+
else
|
504
|
+
@aggregate_klasses
|
505
|
+
end
|
506
|
+
end
|
507
|
+
|
508
|
+
def self.set_aggregate_klasses(arg) # :nodoc:
|
509
|
+
@aggregate_klasses = arg
|
510
|
+
end
|
511
|
+
# }}}
|
512
|
+
|
513
|
+
##########################################################################
|
514
|
+
# @has_a_klasses getters/setters {{{
|
515
|
+
# don't collect is_a from ancestors!
|
516
|
+
|
517
|
+
def self.get_has_a_klasses # :nodoc:
|
518
|
+
if @has_a_klasses.nil?
|
519
|
+
Hash.new
|
520
|
+
else
|
521
|
+
@has_a_klasses
|
522
|
+
end
|
523
|
+
end
|
524
|
+
def self.set_has_a_klasses(arg) # :nodoc:
|
525
|
+
@has_a_klasses = arg
|
526
|
+
end
|
527
|
+
# }}}
|
528
|
+
|
529
|
+
##########################################################################
|
530
|
+
# @explicit getters/setters {{{
|
531
|
+
|
532
|
+
def self.get_explicit # :nodoc:
|
533
|
+
if @explicit.nil?
|
534
|
+
Hash.new
|
535
|
+
else
|
536
|
+
@explicit
|
537
|
+
end
|
538
|
+
end # def
|
539
|
+
def self.set_explicit(arg) # :nodoc:
|
540
|
+
@explicit = arg
|
541
|
+
end # def
|
542
|
+
# }}}
|
543
|
+
|
544
|
+
def self.cache_entities
|
545
|
+
@cache_entities = true
|
546
|
+
end
|
547
|
+
def self.cache_entities?
|
548
|
+
return false # Temp defunc
|
549
|
+
@cache_entities
|
550
|
+
end
|
551
|
+
|
552
|
+
protected
|
553
|
+
|
554
|
+
def self.add_input_filter(attr_name, &block)
|
555
|
+
@input_filters = Hash.new if @input_filters.nil?
|
556
|
+
@input_filters[attr_name] = Proc.new &block
|
557
|
+
end
|
558
|
+
|
559
|
+
def self.add_output_filter(attr_name, &block)
|
560
|
+
@output_filters = Hash.new if @output_filters.nil?
|
561
|
+
@output_filters[attr_name] = Proc.new &block
|
562
|
+
end
|
563
|
+
|
564
|
+
def self.validates(attrib, constraints)
|
565
|
+
# {{{
|
566
|
+
if attrib.kind_of? Clause then
|
567
|
+
attrib_split = attrib.to_s.split('.')
|
568
|
+
table = attrib_split[0..-2]
|
569
|
+
attrib = attrib_split[-1]
|
570
|
+
else
|
571
|
+
table = get_table_name
|
572
|
+
end
|
573
|
+
attrib = attrib.intern unless attrib.instance_of? Symbol
|
574
|
+
|
575
|
+
@constraints = Hash.new if @constraints.nil?
|
576
|
+
@constraints[table] = Hash.new if @constraints[table].nil?
|
577
|
+
@constraints[table][attrib] = Hash.new
|
578
|
+
|
579
|
+
if constraints[:mandatory] then
|
580
|
+
add_explicit_attribute(table, attrib.to_s)
|
581
|
+
end
|
582
|
+
if constraints[:type] then
|
583
|
+
# @attribute_types[table][attrib] = constraints[:type]
|
584
|
+
@constraints[table][attrib][:type] = constraints[:type]
|
585
|
+
end
|
586
|
+
if constraints[:format] then
|
587
|
+
@constraints[table][attrib][:format] = constraints[:format]
|
588
|
+
end
|
589
|
+
if constraints[:length] then
|
590
|
+
if constraints[:length].kind_of? Range then
|
591
|
+
@constraints[table][attrib][:minlength] = constraints[:length].first
|
592
|
+
@constraints[table][attrib][:maxlength] = constraints[:length].last
|
593
|
+
else
|
594
|
+
@constraints[table][attrib][:minlength] = constraints[:length]
|
595
|
+
@constraints[table][attrib][:maxlength] = constraints[:length]
|
596
|
+
end
|
597
|
+
end
|
598
|
+
if constraints[:minlength] then
|
599
|
+
@constraints[table][attrib][:minlength] = constraints[:minlength]
|
600
|
+
end
|
601
|
+
if constraints[:maxlength] then
|
602
|
+
@constraints[table][attrib][:maxlength] = constraints[:maxlength]
|
603
|
+
end
|
604
|
+
end # }}}
|
605
|
+
|
606
|
+
def self.get_maxlength()
|
607
|
+
end
|
608
|
+
|
609
|
+
private
|
610
|
+
|
611
|
+
##########################################################################
|
612
|
+
#
|
613
|
+
def self.add_foreign_keys(args)
|
614
|
+
# {{{
|
615
|
+
|
616
|
+
other_table = args.at(0).to_s # first index is table name (see usage)
|
617
|
+
|
618
|
+
new_foreign_keys = Hash.new
|
619
|
+
new_foreign_keys.update(get_foreign_keys)
|
620
|
+
new_foreign_keys[other_table] = args[1...args.length]
|
621
|
+
|
622
|
+
set_foreign_keys(new_foreign_keys)
|
623
|
+
|
624
|
+
end # }}}
|
625
|
+
|
626
|
+
private
|
627
|
+
|
628
|
+
##########################################################################
|
629
|
+
# Add single or multiple primary keys to @primary_keys[args.at(0)]:
|
630
|
+
def self.add_primary_keys(args)
|
631
|
+
# {{{
|
632
|
+
|
633
|
+
other_table = args.at(0) # first index is table name (see usage)
|
634
|
+
|
635
|
+
new_primary_keys = Hash.new
|
636
|
+
new_primary_keys.update(get_primary_keys)
|
637
|
+
|
638
|
+
foreign_p_keys = args[1...args.length]
|
639
|
+
foreign_p_keys.collect! {|elem| elem.to_s}
|
640
|
+
|
641
|
+
if(new_primary_keys[other_table].nil?)
|
642
|
+
new_primary_keys[other_table] = foreign_p_keys
|
643
|
+
else
|
644
|
+
new_primary_keys[other_table] += foreign_p_keys
|
645
|
+
end
|
646
|
+
|
647
|
+
@primary_keys = new_primary_keys
|
648
|
+
|
649
|
+
end # }}}
|
650
|
+
|
651
|
+
private
|
652
|
+
|
653
|
+
##########################################################################
|
654
|
+
# Add one sequence to @sequences[table_name]:
|
655
|
+
def self.add_sequence(table_name, field_name, sequence_name)
|
656
|
+
# {{{
|
657
|
+
|
658
|
+
new_sequences = Hash.new
|
659
|
+
new_sequences.update(get_sequences)
|
660
|
+
|
661
|
+
new_sequences[table_name] = Hash.new
|
662
|
+
if sequence_name != nil
|
663
|
+
new_sequences[table_name][field_name] = sequence_name.to_s
|
664
|
+
end
|
665
|
+
|
666
|
+
set_sequences(new_sequences)
|
667
|
+
|
668
|
+
end # }}}
|
669
|
+
|
670
|
+
private
|
671
|
+
|
672
|
+
##########################################################################
|
673
|
+
def self.add_attributes(attrib_hash)
|
674
|
+
# {{{
|
675
|
+
|
676
|
+
new_attributes = Hash.new
|
677
|
+
new_attributes.update(get_attributes)
|
678
|
+
|
679
|
+
new_attributes.update(attrib_hash)
|
680
|
+
|
681
|
+
set_attributes(new_attributes)
|
682
|
+
|
683
|
+
end # }}}
|
684
|
+
|
685
|
+
private
|
686
|
+
|
687
|
+
##########################################################################
|
688
|
+
def self.add_attribute_types(attrib_hash)
|
689
|
+
# {{{
|
690
|
+
|
691
|
+
new_types = Hash.new
|
692
|
+
new_types.update(get_attribute_types)
|
693
|
+
|
694
|
+
new_types.update(attrib_hash)
|
695
|
+
|
696
|
+
set_attribute_types(new_types)
|
697
|
+
|
698
|
+
end # }}}
|
699
|
+
|
700
|
+
private
|
701
|
+
|
702
|
+
##########################################################################
|
703
|
+
def self.add_explicit(explicit_hash)
|
704
|
+
# {{{
|
705
|
+
|
706
|
+
new_explicit = explicit_hash
|
707
|
+
new_explicit.update(get_explicit)
|
708
|
+
|
709
|
+
set_explicit(new_explicit)
|
710
|
+
|
711
|
+
end # }}}
|
712
|
+
|
713
|
+
private
|
714
|
+
|
715
|
+
##########################################################################
|
716
|
+
def self.add_explicit_attributes(attrib_hash)
|
717
|
+
# {{{
|
718
|
+
|
719
|
+
# new_explicit_attributes = attrib_hash
|
720
|
+
# new_explicit_attributes.update(get_explicit_attributes)
|
721
|
+
# set_explicit_attributes(new_explicit_attributes)
|
722
|
+
|
723
|
+
attrib_hash.each_pair { |table, attribs|
|
724
|
+
if @explicit_attributes and @explicit_attributes[table] then
|
725
|
+
@explicit_attributes[table] = attrib_hash[table] + @explicit_attributes[table]
|
726
|
+
else
|
727
|
+
@explicit_attributes = Hash.new unless @explicit_attributes
|
728
|
+
@explicit_attributes[table] = attrib_hash[table]
|
729
|
+
end
|
730
|
+
}
|
731
|
+
|
732
|
+
end # }}}
|
733
|
+
|
734
|
+
private
|
735
|
+
|
736
|
+
##########################################################################
|
737
|
+
def self.add_implicit_attributes(attrib_hash)
|
738
|
+
# {{{
|
739
|
+
|
740
|
+
attrib_hash.each_pair { |table, attribs|
|
741
|
+
if @implicit_attributes and @implicit_attributes[table] then
|
742
|
+
@implicit_attributes[table] = attrib_hash[table] + @implicit_attributes[table]
|
743
|
+
else
|
744
|
+
@implicit_attributes = Hash.new unless @implicit_attributes
|
745
|
+
@implicit_attributes[table] = attrib_hash[table]
|
746
|
+
end
|
747
|
+
}
|
748
|
+
|
749
|
+
# new_implicit_attributes = attrib_hash
|
750
|
+
# new_implicit_attributes.update(existing_implicit_attributes)
|
751
|
+
|
752
|
+
# set_implicit_attributes(new_implicit_attributes)
|
753
|
+
end # }}}
|
754
|
+
|
755
|
+
protected
|
756
|
+
|
757
|
+
# Demands a value to be set for create and update procedures.
|
758
|
+
def self.expects(attrib_name, klass=nil)
|
759
|
+
# {{{
|
760
|
+
|
761
|
+
if klass.nil? then table = get_table_name
|
762
|
+
else table = klass.table end
|
763
|
+
|
764
|
+
add_explicit_attribute(table, attrib_name.to_s)
|
765
|
+
|
766
|
+
end # }}}
|
767
|
+
|
768
|
+
def self.hide_attribute(attrib_name)
|
769
|
+
# {{{
|
770
|
+
@hidden_attributes = Hash.new if @hidden_attributes.nil?
|
771
|
+
@hidden_attributes[get_table_name] = Array.new if @hidden_attributes[get_table_name].nil?
|
772
|
+
@hidden_attributes[get_table_name] << attrib_name
|
773
|
+
end
|
774
|
+
|
775
|
+
def self.get_hidden_attributes()
|
776
|
+
return @hidden_attributes if @hidden_attributes
|
777
|
+
Hash.new
|
778
|
+
end # }}}
|
779
|
+
|
780
|
+
private
|
781
|
+
|
782
|
+
##########################################################################
|
783
|
+
#
|
784
|
+
def self.add_explicit_attribute(table_name, attrib_name)
|
785
|
+
# {{{
|
786
|
+
|
787
|
+
table_explicit_attributes = get_explicit_attributes[table_name]
|
788
|
+
if(!table_explicit_attributes.nil?)
|
789
|
+
then table_explicit_attributes.push(attrib_name)
|
790
|
+
else table_explicit_attributes = [attrib_name]
|
791
|
+
end
|
792
|
+
|
793
|
+
new_explicit_attributes =
|
794
|
+
get_explicit_attributes.update({table_name => table_explicit_attributes})
|
795
|
+
|
796
|
+
set_explicit_attributes(new_explicit_attributes)
|
797
|
+
|
798
|
+
end # }}}
|
799
|
+
|
800
|
+
private
|
801
|
+
|
802
|
+
def self.add_implicit_attribute(table_name, attrib_name)
|
803
|
+
# {{{
|
804
|
+
table_implicit_attributes = get_implicit_attributes[table_name]
|
805
|
+
if(!table_implicit_attributes.nil?)
|
806
|
+
then table_implicit_attributes.push(attrib_name)
|
807
|
+
else table_implicit_attributes = [attrib_name]
|
808
|
+
end
|
809
|
+
|
810
|
+
new_implicit_attributes =
|
811
|
+
get_implicit_attributes.update({table_name => table_implicit_attributes})
|
812
|
+
|
813
|
+
set_implicit_attributes(new_implicit_attributes)
|
814
|
+
|
815
|
+
end # }}}
|
816
|
+
|
817
|
+
##########################################################################
|
818
|
+
|
819
|
+
public
|
820
|
+
|
821
|
+
# Returns full attribute name of given attribute
|
822
|
+
def self.[](attribute_name)
|
823
|
+
# {{{
|
824
|
+
return get_table_name+'.'+attribute_name.to_s
|
825
|
+
end # }}}
|
826
|
+
|
827
|
+
# Constructur is private as class is initialized
|
828
|
+
# via .create or .load:
|
829
|
+
|
830
|
+
public
|
831
|
+
|
832
|
+
##########################################################################
|
833
|
+
# Constructor is usually wrapped by e.g. self.load or
|
834
|
+
# self.marshal_load.
|
835
|
+
# Constructor just accepts a value hash, and returns a Table_Accessor
|
836
|
+
# instance holding it.
|
837
|
+
# Note that this method is operating on a Table_Accessor instance, not
|
838
|
+
# on class Table_Accessor itself.
|
839
|
+
def initialize(instance_attrib_values, cache=nil)
|
840
|
+
# {{{
|
841
|
+
|
842
|
+
@loaded_from_cache = (cache == :cached)
|
843
|
+
# set instance variables.
|
844
|
+
# class instance variables are made accessible
|
845
|
+
# in Table_Instance.setup_instance
|
846
|
+
|
847
|
+
if @loaded_from_cache then
|
848
|
+
@attribute_values = instance_attrib_values
|
849
|
+
else
|
850
|
+
@attribute_values = Hash.new
|
851
|
+
values = instance_attrib_values
|
852
|
+
field_index = 0
|
853
|
+
self.class.get_all_table_names.each { |table|
|
854
|
+
@attribute_values[table] = Hash.new
|
855
|
+
field_names = self.class.get_attributes[table]
|
856
|
+
for attr_index in 0...field_names.length do
|
857
|
+
@attribute_values[table][field_names[attr_index]] = values[field_index]
|
858
|
+
field_index += 1
|
859
|
+
end
|
860
|
+
}
|
861
|
+
end
|
862
|
+
# @attribute_values.update(instance_attrib_values)
|
863
|
+
|
864
|
+
setup_instance()
|
865
|
+
|
866
|
+
end # }}}
|
867
|
+
|
868
|
+
public
|
869
|
+
|
870
|
+
##########################################################################
|
871
|
+
# If this model is not to be located in a projects default context,
|
872
|
+
# you can tell Cuba which context to use via
|
873
|
+
#
|
874
|
+
# context :other_context_name
|
875
|
+
#
|
876
|
+
def self.context(context_name)
|
877
|
+
# {{{
|
878
|
+
@context = context_name
|
879
|
+
end
|
880
|
+
def self.get_context
|
881
|
+
@context
|
882
|
+
end
|
883
|
+
# }}}
|
884
|
+
|
885
|
+
public
|
886
|
+
|
887
|
+
##########################################################################
|
888
|
+
# use table :tablename if class != table only.
|
889
|
+
def self.table(_table, _schema=nil)
|
890
|
+
# {{{
|
891
|
+
|
892
|
+
set_table_name(_table.to_s)
|
893
|
+
schema(_schema.to_s) unless _schema.nil?
|
894
|
+
|
895
|
+
load_attribute_fields();
|
896
|
+
|
897
|
+
end # }}}
|
898
|
+
|
899
|
+
private
|
900
|
+
|
901
|
+
##########################################################################
|
902
|
+
# use schema :schemaname if there is a schema only.
|
903
|
+
# @schema getters/setters {{{
|
904
|
+
def self.schema(_schema)
|
905
|
+
set_table_name( _schema.to_s+'.'+get_table_name )
|
906
|
+
end
|
907
|
+
def self.get_schema # :nodoc:
|
908
|
+
@schema
|
909
|
+
end
|
910
|
+
# }}}
|
911
|
+
|
912
|
+
protected
|
913
|
+
|
914
|
+
##########################################################################
|
915
|
+
# usage in derived classes:
|
916
|
+
# primary_key :some_field, :some_field_sequence_name
|
917
|
+
# primary_key :some_other_field, :some_other_field_sequence_name
|
918
|
+
#
|
919
|
+
# don't extend primary_keys of parent!
|
920
|
+
def self.primary_key(*prim_key)
|
921
|
+
# {{{
|
922
|
+
|
923
|
+
keys = Array.new(1,get_table_name) + Array.new(1,prim_key.at(0))
|
924
|
+
add_primary_keys(keys)
|
925
|
+
|
926
|
+
add_sequence(get_table_name, prim_key.at(0), prim_key.at(1))
|
927
|
+
|
928
|
+
# if sequence is given, this primary key is an implicit attribute:
|
929
|
+
if(prim_key.at(1).nil?) then
|
930
|
+
add_explicit_attribute(get_table_name, prim_key.at(0).to_s)
|
931
|
+
else
|
932
|
+
add_implicit_attribute(get_table_name, prim_key.at(0).to_s)
|
933
|
+
end
|
934
|
+
|
935
|
+
end # }}}
|
936
|
+
|
937
|
+
# Called in Cuba.import_imp_model. Defines prepared statements like
|
938
|
+
# The_Model.by_id(id), The_Model.latest_entries(id, amount) etc.
|
939
|
+
def self.define_default_preps
|
940
|
+
return
|
941
|
+
unless @@prepared_statements[:default_preps] then
|
942
|
+
pkey_attrib_name = table_name + '.' << @primary_keys[table_name].first.to_s
|
943
|
+
prepare(:_by_id, Lore::Type.integer) { |e|
|
944
|
+
e.where(self.__send__(pkey_attrib_name.to_s.split('.')[-1].intern) == Lore::Clause.new('$1'))
|
945
|
+
e.limit(1)
|
946
|
+
}
|
947
|
+
prepare(:_latest, Lore::Type.integer) { |e|
|
948
|
+
e.where(true)
|
949
|
+
e.order_by(pkey_attrib_name, :desc)
|
950
|
+
e.limit(Lore::Clause.new('$1'))
|
951
|
+
}
|
952
|
+
@@prepared_statements[:default_preps] = true
|
953
|
+
end
|
954
|
+
end
|
955
|
+
|
956
|
+
def self.by_id(entity_id)
|
957
|
+
begin
|
958
|
+
return _by_id(entity_id).first
|
959
|
+
rescue ::Exception => excep
|
960
|
+
if @@prepared_statements[:default_preps] then
|
961
|
+
raise excep
|
962
|
+
else
|
963
|
+
raise ::Exception.new(excep.message + ' (call define_default_preps first?)')
|
964
|
+
end
|
965
|
+
end
|
966
|
+
end
|
967
|
+
|
968
|
+
protected
|
969
|
+
|
970
|
+
##########################################################################
|
971
|
+
# usage in derived classes:
|
972
|
+
# has_a :table_a, :foreign_key_1, foreign_key_2, ...
|
973
|
+
# has_a :table_b, :foreign_key_1, foreign_key_2, ...
|
974
|
+
#
|
975
|
+
# note that *foreign_keys fields in _this_ table,
|
976
|
+
# not the foreign one.
|
977
|
+
# we want foreign_keys to look like: hash { 'table_a' => array {'id',
|
978
|
+
# 'field' }
|
979
|
+
# 'table_b' => array {'table_b_id'}
|
980
|
+
# thus allowing key tuples and free naming of table fields.
|
981
|
+
def self.has_a(*args)
|
982
|
+
# {{{
|
983
|
+
|
984
|
+
if (args.length < 2) then
|
985
|
+
raise Lore::Exception::Invalid_Usage.new('has_a expects at least Type, :foreign_key_name');
|
986
|
+
end
|
987
|
+
|
988
|
+
accessor = args.at(0)
|
989
|
+
|
990
|
+
foreign_table_name = accessor.get_table_name
|
991
|
+
# convert schema.table => table:
|
992
|
+
foreign_table = foreign_table_name.split('.').at(1)
|
993
|
+
# convert Module::Other::Klass_Name => klass_name:
|
994
|
+
foreign_name = accessor.to_s.split('::').at(-1).downcase
|
995
|
+
|
996
|
+
foreign_keys = Array.new(1,foreign_table_name) + args[1..-1]
|
997
|
+
add_foreign_keys(foreign_keys)
|
998
|
+
|
999
|
+
new_has_a = Array.new(1,foreign_table_name)
|
1000
|
+
|
1001
|
+
new_has_a = get_has_a + new_has_a
|
1002
|
+
set_has_a(new_has_a)
|
1003
|
+
|
1004
|
+
add_has_a_klass(accessor, args[1..-1])
|
1005
|
+
|
1006
|
+
define_entity_access_methods(accessor, args[1..-1])
|
1007
|
+
|
1008
|
+
end # }}}
|
1009
|
+
|
1010
|
+
def self.maps(*accessors)
|
1011
|
+
@primary_keys = Hash.new
|
1012
|
+
accessors.each { |acc|
|
1013
|
+
acc_pkeys = acc.get_primary_keys
|
1014
|
+
# Define accessor as aggregation. This also defines
|
1015
|
+
# acc's primary keys as own primary keys.
|
1016
|
+
aggregates acc, acc_pkeys
|
1017
|
+
}
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
private
|
1021
|
+
|
1022
|
+
def self.define_attribute_clause_methods(accessor)
|
1023
|
+
|
1024
|
+
if !accessor.get_context.nil? && @context != accessor.get_context then
|
1025
|
+
Lore::Context.enter(accessor.get_context)
|
1026
|
+
context_switched = true
|
1027
|
+
end
|
1028
|
+
if accessor.get_attributes[accessor.get_table_name] then
|
1029
|
+
accessor.get_attributes[accessor.get_table_name].each { |attribute|
|
1030
|
+
# Only define methods on own attributes or on
|
1031
|
+
# attributes only a parent Table_Accessor provides:
|
1032
|
+
if accessor == self || !(get_attributes[get_table_name].include? attribute.to_s) then
|
1033
|
+
attribute_type = accessor.get_attribute_types[accessor.get_table_name][attribute]
|
1034
|
+
# log('Expected attribute type for ' << accessor.get_table_name + '.' << attribute.to_s + ': ' << Lore::Type.type_name(attribute_type).to_s + '(' << attribute_type.to_s + ')' )
|
1035
|
+
method =
|
1036
|
+
"def self.#{attribute}()
|
1037
|
+
Clause.new('#{accessor.get_table_name}.#{attribute}', '', '', { :add_types => [ #{attribute_type}], :add_name => '' })
|
1038
|
+
end"
|
1039
|
+
class_eval(method)
|
1040
|
+
|
1041
|
+
"def #{attribute}()
|
1042
|
+
attr(:#{attribute})
|
1043
|
+
end"
|
1044
|
+
class_eval(method)
|
1045
|
+
|
1046
|
+
method =
|
1047
|
+
"def set_#{attribute}(value)
|
1048
|
+
set_attribute_value(:#{attribute.to_s}, value)
|
1049
|
+
end"
|
1050
|
+
class_eval(method)
|
1051
|
+
|
1052
|
+
method =
|
1053
|
+
"def #{attribute}=(value)
|
1054
|
+
set_attribute_value(:#{attribute.to_s}, value)
|
1055
|
+
end"
|
1056
|
+
class_eval(method)
|
1057
|
+
|
1058
|
+
end
|
1059
|
+
}
|
1060
|
+
end
|
1061
|
+
Lore::Context.leave if context_switched
|
1062
|
+
|
1063
|
+
end
|
1064
|
+
|
1065
|
+
private
|
1066
|
+
|
1067
|
+
# Meta-programs class instance methods for accessing types
|
1068
|
+
# associated via has_a.
|
1069
|
+
def self.define_entity_access_methods(accessor, foreign_keys, type_name=nil)
|
1070
|
+
|
1071
|
+
type_name = accessor.to_s.split('::').at(-1).downcase unless type_name
|
1072
|
+
|
1073
|
+
log('defining method '+type_name+'_entity')
|
1074
|
+
define_method(type_name+'_entity') {
|
1075
|
+
|
1076
|
+
has_a_keys = Hash.new
|
1077
|
+
foreign_keys.each { |foreign_key|
|
1078
|
+
# self.<foreign_key> will return corresponding value
|
1079
|
+
# no matter if this klass or a super klass is holding it
|
1080
|
+
has_a_keys[accessor[foreign_key]] = self.__send__ foreign_key
|
1081
|
+
}
|
1082
|
+
return accessor.load(has_a_keys)
|
1083
|
+
}
|
1084
|
+
|
1085
|
+
log('defining method set_'+type_name+'_entity')
|
1086
|
+
define_method('set_'+type_name+'_entity') { |other|
|
1087
|
+
foreign_keys.each { |foreign_key|
|
1088
|
+
# other.<foreign_key> will return corresponding value
|
1089
|
+
# no matter if this klass or a super klass is holding it
|
1090
|
+
self[foreign_key] = other.__send__ foreign_key
|
1091
|
+
}
|
1092
|
+
}
|
1093
|
+
define_method('set_'+type_name+'_entity!') { |other|
|
1094
|
+
self.__send__ 'set_'+type_name+'_entity', other
|
1095
|
+
self.commit
|
1096
|
+
}
|
1097
|
+
|
1098
|
+
end
|
1099
|
+
|
1100
|
+
private
|
1101
|
+
|
1102
|
+
# Meta-programs class instance methods for accessing types
|
1103
|
+
# associated via has_n.
|
1104
|
+
def self.define_entities_access_methods(accessor, values)
|
1105
|
+
|
1106
|
+
type_name = accessor.to_s.split('::').at(-1).downcase unless type_name
|
1107
|
+
|
1108
|
+
log('defining method add_'+type_name+'_entity')
|
1109
|
+
define_method('add_'+type_name+'_entity') { |values|
|
1110
|
+
values.update(get_primary_key_values)
|
1111
|
+
accessor.create(values)
|
1112
|
+
}
|
1113
|
+
log('defining method '+type_name+'_entities')
|
1114
|
+
define_method(type_name+'_entities') {
|
1115
|
+
foreign_key_values = Hash.new
|
1116
|
+
|
1117
|
+
accessor.get_foreign_keys[self.table_name].each { |key|
|
1118
|
+
foreign_key_values[key] = get_attribute_value[key]
|
1119
|
+
}
|
1120
|
+
accessor.all_with(foreign_key_values)
|
1121
|
+
}
|
1122
|
+
|
1123
|
+
end
|
1124
|
+
|
1125
|
+
protected
|
1126
|
+
|
1127
|
+
##########################################################################
|
1128
|
+
# usage in derived classes:
|
1129
|
+
# has_a_named :table_a, :a_foo, :foreign_key_1, :foreign_key_2, ...
|
1130
|
+
# has_a_named :table_a, :a_bar, :foreign_key_1, :foreign_key_2, ...
|
1131
|
+
#
|
1132
|
+
# This method allows one type to aggregate the same type twice, referencing
|
1133
|
+
# them under different names, such as (see example) get_a_foo(), get_a_bar().
|
1134
|
+
def self.has_a_named(*args)
|
1135
|
+
# {{{
|
1136
|
+
|
1137
|
+
if (args.length < 3) then
|
1138
|
+
raise Lore::Model_Exception.new(self, 'has_a_named expects at least :Type, :handle_name, :foreign_key_name');
|
1139
|
+
end
|
1140
|
+
|
1141
|
+
foreign_table_name = args.at(0).get_table_name
|
1142
|
+
# convert schema.table => table:
|
1143
|
+
foreign_table = args.at(0).get_table_name.split('.').at(1)
|
1144
|
+
# convert Module::Other::Klass_Name => klass_name:
|
1145
|
+
foreign_name = args.at(0).to_s.split('::')[-1].downcase
|
1146
|
+
|
1147
|
+
foreign_keys = Array.new(1,foreign_table_name) + args[2...args.length]
|
1148
|
+
add_foreign_keys(foreign_keys)
|
1149
|
+
|
1150
|
+
new_has_a = Array.new(1,foreign_table_name)
|
1151
|
+
new_has_a = get_has_a + new_has_a
|
1152
|
+
set_has_a(new_has_a)
|
1153
|
+
|
1154
|
+
# TODO: To be wrapped by Table_Instance, feeding this method with
|
1155
|
+
# primary keys required to get referenced has_a tuple.
|
1156
|
+
if args.at(2).nil? then type_name = foreign_name
|
1157
|
+
else type_name = args.at(2).to_s
|
1158
|
+
end
|
1159
|
+
|
1160
|
+
log('defining method get_'+type_name)
|
1161
|
+
|
1162
|
+
define_method('get_'+type_name) {
|
1163
|
+
# see self.has_a
|
1164
|
+
}
|
1165
|
+
|
1166
|
+
log('defining method set_'+type_name)
|
1167
|
+
# TODO: To be wrapped by Table_Instance, feeding this method with
|
1168
|
+
# primary keys required to set referenced has_a tuple.
|
1169
|
+
define_method('set_'+type_name) {|*keys|
|
1170
|
+
# see self.has_a
|
1171
|
+
}
|
1172
|
+
|
1173
|
+
end # }}}
|
1174
|
+
|
1175
|
+
protected
|
1176
|
+
|
1177
|
+
def self.belongs_to(*args)
|
1178
|
+
parent = args[0]
|
1179
|
+
foreign_table_name = parent.get_table_name
|
1180
|
+
own_foreign_keys = args[1..-1]
|
1181
|
+
|
1182
|
+
own_foreign_keys = own_foreign_keys.map { |x| x = x.to_s }
|
1183
|
+
add_explicit_attributes(get_table_name => own_foreign_keys)
|
1184
|
+
|
1185
|
+
args[1..-1].each { |a| hide_attribute(a.to_s) }
|
1186
|
+
end
|
1187
|
+
|
1188
|
+
##########################################################################
|
1189
|
+
# usage in derived classes:
|
1190
|
+
# has_n :table_a, :foreign_key_1, foreign_key_2, ...
|
1191
|
+
# has_n :table_b, :foreign_key_1, foreign_key_2, ...
|
1192
|
+
def self.has_n(*args)
|
1193
|
+
# {{{
|
1194
|
+
|
1195
|
+
if (args.length < 2) then
|
1196
|
+
raise Lore::Exception::Invalid_Parameter.new('has_n expects at least :Type, :foreign_key_name');
|
1197
|
+
end
|
1198
|
+
|
1199
|
+
foreign_table_name = args.at(0).get_table_name
|
1200
|
+
# convert schema.table => table:
|
1201
|
+
foreign_table = args.at(0).get_table_name.split('.').at(1)
|
1202
|
+
# convert Module::Other::Klass_Name => klass_name:
|
1203
|
+
foreign_name = args.at(0).to_s.split('::')[-1].downcase
|
1204
|
+
|
1205
|
+
foreign_keys = Array.new(1,foreign_table_name) + args[1...args.length]
|
1206
|
+
add_foreign_keys(foreign_keys)
|
1207
|
+
|
1208
|
+
new_has_n = Array.new(1,foreign_table_name)
|
1209
|
+
new_has_n = get_has_n + new_has_n
|
1210
|
+
set_has_n(new_has_n)
|
1211
|
+
|
1212
|
+
method_suffix = foreign_name
|
1213
|
+
|
1214
|
+
log('defining method '+method_suffix+'_entities')
|
1215
|
+
# TODO: To be wrapped by Table_Instance, feeding this method with
|
1216
|
+
# primary keys required to get referenced has_n tuples.
|
1217
|
+
define_method(method_suffix+'_entities') { |*key_values|
|
1218
|
+
|
1219
|
+
query_args = Hash.new
|
1220
|
+
query_args['_table'] = foreign_table_name
|
1221
|
+
for arg_counter in 0...key_values.length do
|
1222
|
+
|
1223
|
+
query_args[@foreign_keys[foreign_table_name][arg_counter]] = key_values[arg_counter]
|
1224
|
+
|
1225
|
+
end
|
1226
|
+
#Table_Selector.perform(query_args)
|
1227
|
+
}
|
1228
|
+
# attribs is expected to be a Hash { 'field'=>'value', ... }
|
1229
|
+
log('defining method add_'+method_suffix)
|
1230
|
+
# TODO: To be wrapped by Table_Instance, feeding this method with
|
1231
|
+
# primary keys and attribute values required to add referenced
|
1232
|
+
# has_n tuples.
|
1233
|
+
define_method('add_'+method_suffix) {|*attribs|
|
1234
|
+
|
1235
|
+
#query_args = Hash.new
|
1236
|
+
#query_args['_table'] = foreign_table_name
|
1237
|
+
#query_args.update(attribs)
|
1238
|
+
|
1239
|
+
#Table_Inserter.perform(query_args)
|
1240
|
+
}
|
1241
|
+
|
1242
|
+
log('defining method remove_'+method_suffix)
|
1243
|
+
# TODO: To be wrapped by Table_Instance, feeding this method with
|
1244
|
+
# primary keys required to remove a referenced has_n tuple.
|
1245
|
+
define_method('remove_'+method_suffix) {|*keys|
|
1246
|
+
|
1247
|
+
query_args = Hash.new
|
1248
|
+
query_args['_table'] = foreign_table_name
|
1249
|
+
for arg_counter in 0...keys.length do
|
1250
|
+
|
1251
|
+
#query_args[@foreign_keys[foreign_table_name][arg_counter]] = keys[arg_counter]
|
1252
|
+
|
1253
|
+
end
|
1254
|
+
#Table_Deleter.perform(query_args)
|
1255
|
+
}
|
1256
|
+
|
1257
|
+
end # }}}
|
1258
|
+
|
1259
|
+
protected
|
1260
|
+
|
1261
|
+
##########################################################################
|
1262
|
+
# usage in derived classes:
|
1263
|
+
# is_a Other::Module::Other_Klass
|
1264
|
+
# is_a Another::Module::Another_Klass
|
1265
|
+
#
|
1266
|
+
# Effects:
|
1267
|
+
# If foo.is_a bar then creating a foo will also create a bar instance.
|
1268
|
+
# Also, loading a foo will aggregate bar automatically.
|
1269
|
+
def self.is_a(*args)
|
1270
|
+
# {{{
|
1271
|
+
|
1272
|
+
parent = args.at(0)
|
1273
|
+
|
1274
|
+
# before: parent_pkeys = Hash { Parent => [id, name] }
|
1275
|
+
# after: @@primary_keys = Hash { Parent => [id, name]
|
1276
|
+
# This => [this, key] }
|
1277
|
+
|
1278
|
+
if @input_filters.nil? then
|
1279
|
+
@input_filters = parent.get_input_filters
|
1280
|
+
else
|
1281
|
+
@input_filters.update parent.get_input_filters if parent.get_input_filters
|
1282
|
+
end
|
1283
|
+
|
1284
|
+
if @output_filters.nil? then
|
1285
|
+
@output_filters = parent.get_output_filters
|
1286
|
+
else
|
1287
|
+
@output_filters.update parent.get_output_filters if parent.get_output_filters
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
@primary_keys.update(parent.get_primary_keys)
|
1291
|
+
|
1292
|
+
@sequences.update(parent.get_sequences)
|
1293
|
+
# only add parent's own attributes:
|
1294
|
+
add_attributes(parent.get_attributes)
|
1295
|
+
add_attribute_types(parent.get_attribute_types)
|
1296
|
+
|
1297
|
+
add_is_a(parent.get_table_name, parent.get_is_a)
|
1298
|
+
add_is_a_klass(parent, @table_name.to_s+'.' << args.at(1).to_s)
|
1299
|
+
|
1300
|
+
parent.get_has_a_klasses.each_pair { |foreign_key, klass|
|
1301
|
+
has_a klass, foreign_key.split('.')[-1]
|
1302
|
+
}
|
1303
|
+
set_has_a_klasses(get_has_a_klasses.update(parent.get_has_a_klasses))
|
1304
|
+
|
1305
|
+
add_explicit(parent.get_explicit)
|
1306
|
+
|
1307
|
+
add_explicit_attributes(parent.get_explicit_attributes)
|
1308
|
+
add_implicit_attributes(parent.get_implicit_attributes)
|
1309
|
+
|
1310
|
+
@foreign_keys = Hash.new if @foreign_keys.nil?
|
1311
|
+
@foreign_keys.update(parent.get_foreign_keys)
|
1312
|
+
|
1313
|
+
foreign_keys = Array.new(1,parent.get_table_name) + args[1...args.length]
|
1314
|
+
|
1315
|
+
add_foreign_keys(foreign_keys)
|
1316
|
+
|
1317
|
+
get_foreign_keys[parent.get_table_name].each { |attrib|
|
1318
|
+
# add implicit attribute on *own* table, as it is implicit *here*,
|
1319
|
+
# not in the foreign table
|
1320
|
+
add_implicit_attribute(get_table_name, attrib.to_s)
|
1321
|
+
}
|
1322
|
+
|
1323
|
+
define_attribute_clause_methods(parent)
|
1324
|
+
|
1325
|
+
end # }}}
|
1326
|
+
|
1327
|
+
protected
|
1328
|
+
|
1329
|
+
##########################################################################
|
1330
|
+
# usage in derived classes:
|
1331
|
+
# aggregates Other::Module::Other_Klass
|
1332
|
+
# aggregates Another::Module::Another_Klass
|
1333
|
+
#
|
1334
|
+
# Effects:
|
1335
|
+
# Unlike is_a, If foo.aggregates bar then creating/deleting a foo will not create/delete
|
1336
|
+
# a bar instance, but loading a foo will aggregate bar automatically, like
|
1337
|
+
# is_a.
|
1338
|
+
def self.aggregates(*args)
|
1339
|
+
# {{{
|
1340
|
+
|
1341
|
+
parent = args.at(0)
|
1342
|
+
|
1343
|
+
# before: parent_pkeys = Hash { Parent => [id, name] }
|
1344
|
+
# after: @@primary_keys = Hash { Parent => [id, name]
|
1345
|
+
# This => [this, key] }
|
1346
|
+
@primary_keys.update(parent.get_primary_keys)
|
1347
|
+
|
1348
|
+
# only add parent's own attributes:
|
1349
|
+
add_attributes(parent.get_attributes)
|
1350
|
+
add_attribute_types(parent.get_attribute_types)
|
1351
|
+
|
1352
|
+
add_aggregate(parent.get_table_name, parent.get_is_a)
|
1353
|
+
add_aggregate_klass(parent, args[1..-1])
|
1354
|
+
|
1355
|
+
set_has_a_klasses(get_has_a_klasses.update(parent.get_has_a_klasses))
|
1356
|
+
|
1357
|
+
# Hide aggregated attributes for create, update and form
|
1358
|
+
# generation:
|
1359
|
+
add_implicit_attributes(parent.get_attributes)
|
1360
|
+
add_implicit_attributes(parent.get_implicit_attributes)
|
1361
|
+
|
1362
|
+
foreign_keys = Array.new(1,parent.get_table_name) + args[1..-1]
|
1363
|
+
|
1364
|
+
add_foreign_keys(foreign_keys)
|
1365
|
+
|
1366
|
+
get_foreign_keys[parent.get_table_name].each { |attrib|
|
1367
|
+
# add implicit attribute on *own* table, as it is implicit *here*,
|
1368
|
+
# not in the foreign table
|
1369
|
+
add_implicit_attribute(get_table_name, attrib.to_s)
|
1370
|
+
}
|
1371
|
+
|
1372
|
+
use_label(parent.get_labels)
|
1373
|
+
|
1374
|
+
define_entity_access_methods(parent, args[1..-1])
|
1375
|
+
|
1376
|
+
end # }}}
|
1377
|
+
|
1378
|
+
private
|
1379
|
+
|
1380
|
+
def self.add_is_a(table_name, derive_tree)
|
1381
|
+
# {{{
|
1382
|
+
|
1383
|
+
new_is_a = Hash.new
|
1384
|
+
new_is_a[table_name] = derive_tree
|
1385
|
+
new_is_a.update(get_is_a)
|
1386
|
+
|
1387
|
+
set_is_a(new_is_a)
|
1388
|
+
|
1389
|
+
end # }}}
|
1390
|
+
|
1391
|
+
private
|
1392
|
+
|
1393
|
+
def self.add_is_a_klass(klass, foreign_key)
|
1394
|
+
# {{{
|
1395
|
+
|
1396
|
+
@is_a_klasses = Hash.new if @is_a_klasses.nil?
|
1397
|
+
@is_a_klasses[foreign_key] = klass
|
1398
|
+
|
1399
|
+
end # }}}
|
1400
|
+
|
1401
|
+
private
|
1402
|
+
|
1403
|
+
def self.add_aggregate(table_name, derive_tree)
|
1404
|
+
# {{{
|
1405
|
+
|
1406
|
+
new_aggregates = Hash.new
|
1407
|
+
new_aggregates[table_name] = derive_tree
|
1408
|
+
new_aggregates.update(get_aggregates)
|
1409
|
+
|
1410
|
+
set_aggregates(new_aggregates)
|
1411
|
+
|
1412
|
+
end # }}}
|
1413
|
+
|
1414
|
+
private
|
1415
|
+
|
1416
|
+
def self.add_aggregate_klass(klass, attributes)
|
1417
|
+
# {{{
|
1418
|
+
|
1419
|
+
new_aggregate_klasses = get_aggregate_klasses
|
1420
|
+
attributes.each { |attribute|
|
1421
|
+
new_aggregate_klasses[get_table_name+'.' << attribute.to_s] = klass
|
1422
|
+
}
|
1423
|
+
|
1424
|
+
set_aggregate_klasses(new_aggregate_klasses)
|
1425
|
+
|
1426
|
+
end # }}}
|
1427
|
+
|
1428
|
+
private
|
1429
|
+
|
1430
|
+
def self.add_has_a_klass(klass, attributes)
|
1431
|
+
# {{{
|
1432
|
+
|
1433
|
+
new_has_a_klasses = get_has_a_klasses
|
1434
|
+
attributes.each { |attribute|
|
1435
|
+
new_has_a_klasses[get_table_name+'.' << attribute.to_s] = klass
|
1436
|
+
}
|
1437
|
+
|
1438
|
+
set_has_a_klasses(new_has_a_klasses)
|
1439
|
+
|
1440
|
+
end # }}}
|
1441
|
+
|
1442
|
+
protected
|
1443
|
+
# Tell Table_Accessor to ignore attribute in update commands
|
1444
|
+
# unless it has a non-empty value.
|
1445
|
+
def self.explicit(*args)
|
1446
|
+
# {{{
|
1447
|
+
|
1448
|
+
new_explicit = get_explicit
|
1449
|
+
new_explicit[get_table_name] = args.map!{|elem| elem.to_s}
|
1450
|
+
|
1451
|
+
add_explicit(new_explicit)
|
1452
|
+
|
1453
|
+
end # }}}
|
1454
|
+
|
1455
|
+
private
|
1456
|
+
|
1457
|
+
##########################################################################
|
1458
|
+
# Usage:
|
1459
|
+
# Cuba::Admin::Container_Local.select({'primary_key_1'=>'2',
|
1460
|
+
# 'primary_key_2'=>'38',
|
1461
|
+
# 'additionally_where_field'=>'73'
|
1462
|
+
# })
|
1463
|
+
def self.select_row_by_key(*keys)
|
1464
|
+
# {{{
|
1465
|
+
|
1466
|
+
# Aggregate klasses this klass is derived from and
|
1467
|
+
# those it aggregates explicitly:
|
1468
|
+
begin
|
1469
|
+
result = Lore::Table_Selector.select_on_keys(self, keys)
|
1470
|
+
rescue ::Exception => excep
|
1471
|
+
raise excep
|
1472
|
+
raise ::Exception.new('Unable to load instance from DB (Reason: ' << excep.message << ')')
|
1473
|
+
end
|
1474
|
+
field_hash = result.get_row()
|
1475
|
+
|
1476
|
+
end # }}}
|
1477
|
+
|
1478
|
+
public
|
1479
|
+
|
1480
|
+
# Same as select, but with predefined primary key values as
|
1481
|
+
# hash.
|
1482
|
+
def self.select_by_key(*keys)
|
1483
|
+
# {{{
|
1484
|
+
|
1485
|
+
result = Lore::Table_Selector.select_on_keys(self, keys)
|
1486
|
+
|
1487
|
+
field_array = result.get_rows()
|
1488
|
+
|
1489
|
+
end # }}}
|
1490
|
+
|
1491
|
+
public
|
1492
|
+
|
1493
|
+
def self.update(&block)
|
1494
|
+
# {{{
|
1495
|
+
|
1496
|
+
query_string = Lore::Table_Updater.block_update(self, &block)
|
1497
|
+
|
1498
|
+
end # def }}}
|
1499
|
+
|
1500
|
+
public
|
1501
|
+
|
1502
|
+
def self.select(clause=nil, &block)
|
1503
|
+
# {{{
|
1504
|
+
GC.disable
|
1505
|
+
if(!clause.nil? && !clause.to_s.include?('*,')) then
|
1506
|
+
|
1507
|
+
query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
|
1508
|
+
return Clause.new(query_string)
|
1509
|
+
|
1510
|
+
else
|
1511
|
+
|
1512
|
+
what = clause.to_s
|
1513
|
+
result = Lore::Table_Selector.select_cached(what, self, &block)
|
1514
|
+
|
1515
|
+
# result = Array.new
|
1516
|
+
# db_result.get_rows.each { |row|
|
1517
|
+
# result.push(self.new(self, row))
|
1518
|
+
# }
|
1519
|
+
GC.enable
|
1520
|
+
return result
|
1521
|
+
|
1522
|
+
end
|
1523
|
+
|
1524
|
+
end # }}}
|
1525
|
+
|
1526
|
+
public
|
1527
|
+
|
1528
|
+
def self.select_query(clause=nil, &block)
|
1529
|
+
query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
|
1530
|
+
end
|
1531
|
+
|
1532
|
+
# Prepares a query for execution. This offers four advantages:
|
1533
|
+
# - The query doesn't have to be interpreted by the DB every time
|
1534
|
+
# - The query call is available via direct method call.
|
1535
|
+
# - DB validates against types, thus preventing SQL injection
|
1536
|
+
# - It doesn't Lore require to compose the query string again. This
|
1537
|
+
# effects the most significant performance gain (Up to 60% execution
|
1538
|
+
# time in some benchmarks)
|
1539
|
+
# Usage:
|
1540
|
+
#
|
1541
|
+
# Article.prepare(:by_name_and_date, Lore::Type::Integer, Lore::Type::Date) { |a,fields|
|
1542
|
+
# a.where((Article.article_id == fields[0] &
|
1543
|
+
# (Article.date == fields[1]))
|
1544
|
+
# }
|
1545
|
+
# Article.by_name_and_date('Merry Christmas', '20081224')
|
1546
|
+
#
|
1547
|
+
# From the PostgreSQL 7.4 Manual:
|
1548
|
+
#
|
1549
|
+
# "In some situations, the query plan produced by for a prepared statement may be
|
1550
|
+
# inferior to the plan produced if the statement were submitted and executed normally.
|
1551
|
+
# This is because when the statement is planned and the planner attempts to determine
|
1552
|
+
# the optimal query plan, the actual values of any parameters specified in the
|
1553
|
+
# statement are unavailable. PostgreSQL collects statistics on the distribution of
|
1554
|
+
# data in the table, and can use constant values in a statement to make guesses about
|
1555
|
+
# the likely result of executing the statement. Since this data is unavailable when
|
1556
|
+
# planning prepared statements with parameters, the chosen plan may be suboptimal. To
|
1557
|
+
# examine the query plan PostgreSQL has chosen for a prepared statement, use
|
1558
|
+
# EXPLAIN EXECUTE. "
|
1559
|
+
#
|
1560
|
+
def self.prepare(plan_name, *args, &block)
|
1561
|
+
log('PREPARE: TRYING CLASS METHOD ' << plan_name.to_s)
|
1562
|
+
if !@@prepared_statements[plan_name] then
|
1563
|
+
Table_Selector.prepare(plan_name, self, args, &block)
|
1564
|
+
|
1565
|
+
log('PREPARE: CREATE CLASS METHOD ' << plan_name.to_s)
|
1566
|
+
instance_eval("
|
1567
|
+
def #{plan_name.to_s}(*args)
|
1568
|
+
execute_prepared(:#{plan_name}, args)
|
1569
|
+
end")
|
1570
|
+
@@prepared_statements[plan_name] = true
|
1571
|
+
log('PREPARE: CREATED CLASS METHOD ' << plan_name.to_s)
|
1572
|
+
end
|
1573
|
+
end
|
1574
|
+
|
1575
|
+
def self.execute_prepared(plan_name, *args)
|
1576
|
+
plan_name = "#{table_name.gsub('.','_')}__#{plan_name.to_s}"
|
1577
|
+
Table_Selector.select_prepared(plan_name, self, args)
|
1578
|
+
end
|
1579
|
+
|
1580
|
+
public
|
1581
|
+
|
1582
|
+
# Same as select, but returns
|
1583
|
+
def self.select_value(what, &block)
|
1584
|
+
# {{{
|
1585
|
+
db_result = Lore::Table_Selector.select(what, self, &block)
|
1586
|
+
row = db_result.get_row
|
1587
|
+
return row[''] if row
|
1588
|
+
return {}
|
1589
|
+
end # }}}
|
1590
|
+
|
1591
|
+
public
|
1592
|
+
|
1593
|
+
def self.select_values(what, &block)
|
1594
|
+
# {{{
|
1595
|
+
db_result = Lore::Table_Selector.select(what, self, &block)
|
1596
|
+
return db_result.get_rows[:values].map { |e|
|
1597
|
+
e.first
|
1598
|
+
}
|
1599
|
+
end # }}}
|
1600
|
+
|
1601
|
+
public
|
1602
|
+
|
1603
|
+
##########################################################################
|
1604
|
+
# Wrap explicit select. Example:
|
1605
|
+
# SomeModule::SomeAccessor.explicit_insert({
|
1606
|
+
# table_name_A =>
|
1607
|
+
# {'some_field'=>'2',
|
1608
|
+
# 'other_field'=>'3'},
|
1609
|
+
# table_name_A =>
|
1610
|
+
# {'another_field'=>'5'}
|
1611
|
+
# })
|
1612
|
+
# Note that field in 'field'=>'value' is exactly the
|
1613
|
+
# field name in the table (e.g. table_name_A) it holds.
|
1614
|
+
def self.explicit_insert(keys)
|
1615
|
+
# {{{
|
1616
|
+
Lore::Table_Inserter.perform_insert(self, keys)
|
1617
|
+
end # }}}
|
1618
|
+
|
1619
|
+
|
1620
|
+
private
|
1621
|
+
|
1622
|
+
##########################################################################
|
1623
|
+
# Wrap default select. Example:
|
1624
|
+
# SomeModule::SomeAccessor.insert({
|
1625
|
+
# 'some_field'=>'2',
|
1626
|
+
# 'other_field'=>'3',
|
1627
|
+
# 'another_field'=>'5'
|
1628
|
+
# })
|
1629
|
+
# Note that field in 'field'=>'value' is exactly the
|
1630
|
+
# field name in the table it holds.
|
1631
|
+
# Table_Accessor.insert basically resolves an explicit
|
1632
|
+
# hash and passes it to Table_Accessor.explicit_insert.
|
1633
|
+
def self.insert(keys)
|
1634
|
+
# {{{
|
1635
|
+
|
1636
|
+
key_hash = Hash.new
|
1637
|
+
|
1638
|
+
log('ATTRIBUTES TOTAL: ')
|
1639
|
+
log(get_attributes.inspect)
|
1640
|
+
|
1641
|
+
attribute_name_array = Array.new
|
1642
|
+
attribute_table = ''
|
1643
|
+
get_attributes.each_pair { |table, fields|
|
1644
|
+
|
1645
|
+
key_hash[table] = Hash.new
|
1646
|
+
keys.each_pair { |attribute, value|
|
1647
|
+
|
1648
|
+
attribute = attribute.to_s
|
1649
|
+
attribute_name_array = attribute.split('.')
|
1650
|
+
if attribute_name_array.at(2).nil? then
|
1651
|
+
attribute_table = nil
|
1652
|
+
else
|
1653
|
+
attribute_table = (attribute_name_array.at(0).to_s+'.'+attribute_name_array.at(1).to_s)
|
1654
|
+
end
|
1655
|
+
|
1656
|
+
if fields.include?(attribute) then
|
1657
|
+
key_hash[table][attribute] = value unless (value == '' || value.nil?)
|
1658
|
+
|
1659
|
+
elsif !attribute_table.nil? and
|
1660
|
+
fields.include?(attribute_name_array.at(2)) and
|
1661
|
+
attribute_table == table then
|
1662
|
+
|
1663
|
+
key_hash[table][attribute_name_array.at(2)] = value unless (value == '' || value.nil?)
|
1664
|
+
end
|
1665
|
+
}
|
1666
|
+
|
1667
|
+
}
|
1668
|
+
|
1669
|
+
log(key_hash.inspect)
|
1670
|
+
|
1671
|
+
# sequence values only are known after insert operation,
|
1672
|
+
# so we have to retreive the complete key_hash back from
|
1673
|
+
# Table_Inserter.perform_insert:
|
1674
|
+
key_hash = Lore::Table_Inserter.perform_insert(self, key_hash)
|
1675
|
+
# key_hash has been extended by sequence_values now, so we
|
1676
|
+
# return it:
|
1677
|
+
|
1678
|
+
key_hash
|
1679
|
+
|
1680
|
+
end # }}}
|
1681
|
+
|
1682
|
+
public
|
1683
|
+
|
1684
|
+
##########################################################################
|
1685
|
+
# Returns a new Table_Accessor instance by inserting given attribute
|
1686
|
+
# values into db and returning an instance for further operations.
|
1687
|
+
def self.create(attrib_values)
|
1688
|
+
# {{{
|
1689
|
+
before_create(attrib_values)
|
1690
|
+
|
1691
|
+
values = Hash.new
|
1692
|
+
|
1693
|
+
Lore.log { 'KLASS: ' << attrib_values.class.to_s }
|
1694
|
+
Lore.log { 'INSERTING VALUES: ' << attrib_values.inspect }
|
1695
|
+
input_filters = get_input_filters
|
1696
|
+
attrib_key = ''
|
1697
|
+
attrib_name = ''
|
1698
|
+
|
1699
|
+
attrib_values.each_pair { |attrib_name, attrib_value|
|
1700
|
+
if attrib_name.instance_of? Symbol then
|
1701
|
+
attrib_key = attrib_name
|
1702
|
+
else
|
1703
|
+
attrib_key = attrib_name.split('.')[-1].intern
|
1704
|
+
end
|
1705
|
+
|
1706
|
+
if (input_filters && input_filters[attrib_key]) then
|
1707
|
+
attrib_values[attrib_name] = input_filters[attrib_key].call(attrib_value)
|
1708
|
+
end
|
1709
|
+
}
|
1710
|
+
log('FILTERED VALUES: ' << attrib_values.inspect)
|
1711
|
+
after_filters(attrib_values)
|
1712
|
+
|
1713
|
+
# Predefine
|
1714
|
+
attrib_name_array = Array.new
|
1715
|
+
# distribute attrib names to tables:
|
1716
|
+
get_attributes().each_pair { |table, attribs|
|
1717
|
+
table_values = Hash.new
|
1718
|
+
|
1719
|
+
attrib_name_array = Array.new
|
1720
|
+
log('ALL ATTRIBS: ' << table.to_s + ' <-- ' << attribs.inspect)
|
1721
|
+
attrib_values.each_pair { |attrib_name, attrib_value|
|
1722
|
+
attrib_name = attrib_name.to_s
|
1723
|
+
attrib_name_array = attrib_name.split('.')
|
1724
|
+
|
1725
|
+
if attribs.include? attrib_name then
|
1726
|
+
|
1727
|
+
table_values[attrib_name] = attrib_value
|
1728
|
+
|
1729
|
+
elsif attribs.include? attrib_name_array.at(2) and
|
1730
|
+
attrib_name_array.at(0)+'.'+attrib_name_array.at(1) == table then
|
1731
|
+
table_values[attrib_name_array.at(2)] = attrib_value
|
1732
|
+
end
|
1733
|
+
}
|
1734
|
+
values[table] = table_values
|
1735
|
+
}
|
1736
|
+
|
1737
|
+
begin
|
1738
|
+
|
1739
|
+
before_validation(values)
|
1740
|
+
Lore::Validation::Parameter_Validator.invalid_params(self,
|
1741
|
+
values)
|
1742
|
+
|
1743
|
+
rescue Lore::Exception::Invalid_Klass_Parameters => ikp
|
1744
|
+
|
1745
|
+
# log'n'throw
|
1746
|
+
ikp.log
|
1747
|
+
raise ikp
|
1748
|
+
|
1749
|
+
end
|
1750
|
+
|
1751
|
+
before_insert(attrib_values)
|
1752
|
+
|
1753
|
+
# retreive all final attrib values after insert: (this way, also
|
1754
|
+
# sequence values are resolved):
|
1755
|
+
#
|
1756
|
+
attrib_values = insert(attrib_values)
|
1757
|
+
|
1758
|
+
select_keys = Hash.new
|
1759
|
+
if @primary_keys then
|
1760
|
+
@primary_keys[table_name].each { |key|
|
1761
|
+
select_keys[table_name + '.' << key] = attrib_values[table_name][key]
|
1762
|
+
}
|
1763
|
+
end
|
1764
|
+
|
1765
|
+
# Create klass instance via new:
|
1766
|
+
# Cuba::Module.new(self, attrib_values)
|
1767
|
+
|
1768
|
+
# Create klass instance via load (wrapper of new):
|
1769
|
+
obj = self.load(select_keys)
|
1770
|
+
after_create(obj)
|
1771
|
+
|
1772
|
+
return obj
|
1773
|
+
|
1774
|
+
end # }}}
|
1775
|
+
|
1776
|
+
public
|
1777
|
+
|
1778
|
+
##########################################################################
|
1779
|
+
# Return new Table_Accessor instance by loading an existing entry from
|
1780
|
+
# table.
|
1781
|
+
def self.load(keys)
|
1782
|
+
# {{{
|
1783
|
+
|
1784
|
+
before_load(keys)
|
1785
|
+
|
1786
|
+
select_keys = Hash.new
|
1787
|
+
|
1788
|
+
get_primary_keys[table_name].each { |attrib_name|
|
1789
|
+
value = keys[attrib_name.intern]
|
1790
|
+
value = keys[attrib_name.to_s] if value.nil?
|
1791
|
+
value = keys[table_name+'.'+attrib_name.to_s] if value.nil?
|
1792
|
+
select_keys[table_name+'.'+attrib_name.to_s] = value unless value.nil?
|
1793
|
+
}
|
1794
|
+
|
1795
|
+
return false if select_keys.empty?
|
1796
|
+
|
1797
|
+
log('Loading with prim keys: ' << select_keys.inspect)
|
1798
|
+
instance_attribs_hash = select_row_by_key(select_keys)
|
1799
|
+
|
1800
|
+
return false if instance_attribs_hash.nil?
|
1801
|
+
|
1802
|
+
instance = new(instance_attribs_hash)
|
1803
|
+
|
1804
|
+
#TODO:
|
1805
|
+
# after_load(instance)
|
1806
|
+
|
1807
|
+
return instance
|
1808
|
+
end # }}}
|
1809
|
+
|
1810
|
+
public
|
1811
|
+
|
1812
|
+
##########################################################################
|
1813
|
+
# Delete this object and use Table_Deleter to delete its entity tuple
|
1814
|
+
# from database.
|
1815
|
+
def self.delete(value_keys=nil, &block)
|
1816
|
+
# {{{
|
1817
|
+
|
1818
|
+
if value_keys then
|
1819
|
+
before_delete(value_keys)
|
1820
|
+
Lore::Table_Deleter.perform_delete(self, value_keys)
|
1821
|
+
after_delete(value_keys)
|
1822
|
+
else
|
1823
|
+
Lore::Table_Deleter.block_delete(self, &block)
|
1824
|
+
end
|
1825
|
+
|
1826
|
+
end # }}}
|
1827
|
+
|
1828
|
+
private
|
1829
|
+
|
1830
|
+
##########################################################################
|
1831
|
+
# Send a hollow query to db, thus only field names are returned.
|
1832
|
+
# This works (and has to work) on empty tables, too.
|
1833
|
+
#
|
1834
|
+
# For every attribute, a class method with the attribute's name is defined
|
1835
|
+
# in order to retreive the absolute field name (schema.table.field) by calling
|
1836
|
+
# Table_Accessor.field
|
1837
|
+
def self.load_attribute_fields()
|
1838
|
+
# {{{
|
1839
|
+
if !@attributes.nil? then
|
1840
|
+
return @attributes
|
1841
|
+
end
|
1842
|
+
|
1843
|
+
@attributes = Hash.new
|
1844
|
+
|
1845
|
+
if(get_table_name != '') then
|
1846
|
+
|
1847
|
+
Lore::Context.enter(@context) unless @context.nil?
|
1848
|
+
begin
|
1849
|
+
fields_result = Lore::Connection.perform('SELECT * FROM '+get_table_name+' WHERE false')
|
1850
|
+
ensure
|
1851
|
+
Lore::Context.leave unless @context.nil?
|
1852
|
+
end
|
1853
|
+
|
1854
|
+
own_attributes = Hash.new
|
1855
|
+
own_attributes[get_table_name] = fields_result.get_field_names()
|
1856
|
+
own_types = Hash.new
|
1857
|
+
own_types[get_table_name] = fields_result.get_field_types()
|
1858
|
+
add_attributes(own_attributes)
|
1859
|
+
add_attribute_types(own_types)
|
1860
|
+
|
1861
|
+
Lore.log { 'Defined attributes: ' << @attributes.inspect }
|
1862
|
+
|
1863
|
+
define_attribute_clause_methods(self)
|
1864
|
+
|
1865
|
+
end # if
|
1866
|
+
return @attributes
|
1867
|
+
|
1868
|
+
end # }}}
|
1869
|
+
|
1870
|
+
public
|
1871
|
+
|
1872
|
+
##########################################################################
|
1873
|
+
# Simulates inheritance: Delegate missing methods to parent Table_Accessor.
|
1874
|
+
def self.method_missing(meth)
|
1875
|
+
if @is_a_klasses then
|
1876
|
+
@is_a_klasses.each_pair { |foreign_key, k|
|
1877
|
+
return (k.__send__(meth.to_s)) if k.respond_to? meth
|
1878
|
+
}
|
1879
|
+
end
|
1880
|
+
raise ::Exception.new('Undefined method '<< meth.to_s << ' for ' << self.to_s)
|
1881
|
+
end
|
1882
|
+
|
1883
|
+
public
|
1884
|
+
|
1885
|
+
##########################################################################
|
1886
|
+
# Inspect method
|
1887
|
+
def self.inspect
|
1888
|
+
# {{{
|
1889
|
+
|
1890
|
+
# return self.to_s + ' : Table_Accessor'
|
1891
|
+
|
1892
|
+
dump = 'Accessor: ' << self.to_s
|
1893
|
+
# dump << "\n\t" << 'own table name: '+get_table_name
|
1894
|
+
# dump << "\n\t" << 'attributes: '
|
1895
|
+
# dump << "\n\t" << @attributes.inspect
|
1896
|
+
# dump << "\n\t" << 'explicit attributes: '
|
1897
|
+
# dump << "\n\t" << @explicit_attributes.inspect
|
1898
|
+
# dump << "\n\t" << 'implicit attributes: '
|
1899
|
+
# dump << "\n\t" << @implicit_attributes.inspect
|
1900
|
+
# dump << "\n\t" << 'constraints: '
|
1901
|
+
# dump << "\n\t" << @constraints.inspect
|
1902
|
+
# dump << "\n\t" << 'attribute types: '
|
1903
|
+
# dump << "\n\t" << @attribute_types.inspect
|
1904
|
+
# dump << "\n\t" << 'primary keys: '
|
1905
|
+
# dump << "\n\t" << @primary_keys.inspect
|
1906
|
+
# dump << "\n\t" << 'sequences: '
|
1907
|
+
# dump << "\n\t" << @sequences.inspect
|
1908
|
+
# dump << "\n\t" << 'foreign keys: '
|
1909
|
+
# dump << "\n\t" << @foreign_keys.inspect
|
1910
|
+
# dump << "\n\t" << 'has_a: '
|
1911
|
+
# dump << "\n\t" << @has_a.inspect
|
1912
|
+
# dump << "\n\t" << 'has_n: '
|
1913
|
+
# dump << "\n\t" << @has_n.inspect
|
1914
|
+
# dump << "\n\t" << 'is_a: '
|
1915
|
+
# dump << "\n\t" << @is_a.inspect
|
1916
|
+
# dump << "\n\t" << 'explicit: '
|
1917
|
+
# dump << "\n\t" << @explicit.inspect
|
1918
|
+
# dump << "\n"
|
1919
|
+
|
1920
|
+
end # }}}
|
1921
|
+
|
1922
|
+
end # class
|
1923
|
+
|
1924
|
+
|
1925
|
+
end # module
|
1926
|
+
|