lore 0.4.8 → 0.9.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. data/Manifest.txt +16 -7
  2. data/README.rdoc +91 -0
  3. data/benchmark/benchmark.sql +11 -0
  4. data/benchmark/results.txt +28 -0
  5. data/benchmark/select.rb +352 -0
  6. data/lib/lore.rb +22 -8
  7. data/lib/lore/adapters/context.rb +64 -0
  8. data/lib/lore/adapters/postgres-pr.rb +6 -0
  9. data/lib/lore/adapters/postgres-pr/connection.rb +93 -0
  10. data/lib/lore/adapters/postgres-pr/result.rb +63 -0
  11. data/lib/lore/{types.rb → adapters/postgres-pr/types.rb} +36 -0
  12. data/lib/lore/adapters/postgres.rb +24 -0
  13. data/lib/lore/adapters/postgres/connection.rb +81 -0
  14. data/lib/lore/adapters/postgres/result.rb +82 -0
  15. data/lib/lore/adapters/postgres/types.rb +91 -0
  16. data/lib/lore/bits.rb +18 -0
  17. data/lib/lore/cache/abstract_entity_cache.rb +2 -1
  18. data/lib/lore/cache/cacheable.rb +12 -177
  19. data/lib/lore/cache/memcache_entity_cache.rb +89 -0
  20. data/lib/lore/cache/memory_entity_cache.rb +77 -0
  21. data/lib/lore/cache/mmap_entity_cache.rb +2 -2
  22. data/lib/lore/cache/mmap_entity_cache_bork.rb +86 -0
  23. data/lib/lore/clause.rb +107 -35
  24. data/lib/lore/{exception → exceptions}/ambiguous_attribute.rb +2 -2
  25. data/lib/lore/{exception → exceptions}/cache_exception.rb +1 -1
  26. data/lib/lore/exceptions/database_exception.rb +16 -0
  27. data/lib/lore/{exception/invalid_parameter.rb → exceptions/invalid_field.rb} +7 -4
  28. data/lib/lore/exceptions/unknown_type.rb +18 -0
  29. data/lib/lore/exceptions/validation_failure.rb +71 -0
  30. data/lib/lore/gui/form_generator.rb +109 -60
  31. data/lib/lore/gui/lore_model_select_field.rb +1 -0
  32. data/lib/lore/migration.rb +84 -25
  33. data/lib/lore/model.rb +3 -18
  34. data/lib/lore/{aspect.rb → model/aspect.rb} +0 -0
  35. data/lib/lore/model/associations.rb +225 -0
  36. data/lib/lore/model/attribute_settings.rb +233 -0
  37. data/lib/lore/model/filters.rb +34 -0
  38. data/lib/lore/model/mockable.rb +62 -0
  39. data/lib/lore/{model_factory.rb → model/model_factory.rb} +68 -39
  40. data/lib/lore/model/model_instance.rb +382 -0
  41. data/lib/lore/{model_shortcuts.rb → model/model_shortcuts.rb} +7 -0
  42. data/lib/lore/model/polymorphic.rb +53 -0
  43. data/lib/lore/model/prepare.rb +97 -0
  44. data/lib/lore/model/table_accessor.rb +1016 -0
  45. data/lib/lore/query.rb +71 -0
  46. data/lib/lore/query_shortcuts.rb +43 -11
  47. data/lib/lore/strategies/table_delete.rb +115 -0
  48. data/lib/lore/strategies/table_insert.rb +146 -0
  49. data/lib/lore/strategies/table_select.rb +299 -0
  50. data/lib/lore/strategies/table_update.rb +155 -0
  51. data/lib/lore/validation/parameter_validator.rb +85 -26
  52. data/lib/lore/validation/type_validator.rb +34 -78
  53. data/{custom_models.rb → lore-0.9.2.gem} +0 -0
  54. data/lore.gemspec +26 -17
  55. data/spec/clause.rb +37 -0
  56. data/spec/fixtures/blank_models.rb +37 -0
  57. data/{test/model.rb → spec/fixtures/models.rb} +64 -41
  58. data/spec/fixtures/polymorphic_models.rb +68 -0
  59. data/spec/model_associations.rb +86 -0
  60. data/spec/model_create.rb +47 -0
  61. data/spec/model_definition.rb +151 -0
  62. data/spec/model_delete.rb +31 -0
  63. data/spec/model_inheritance.rb +50 -0
  64. data/spec/model_polymorphic.rb +85 -0
  65. data/spec/model_select.rb +101 -0
  66. data/spec/model_select_eager.rb +42 -0
  67. data/spec/model_union_select.rb +33 -0
  68. data/spec/model_update.rb +45 -0
  69. data/spec/model_validation.rb +20 -0
  70. data/spec/spec_db.sql +808 -0
  71. data/spec/spec_env.rb +19 -0
  72. data/spec/spec_helpers.rb +77 -0
  73. metadata +93 -82
  74. data/lib/lore/README.txt +0 -84
  75. data/lib/lore/behaviours/lockable.rb +0 -55
  76. data/lib/lore/behaviours/movable.rb +0 -72
  77. data/lib/lore/behaviours/paginated.rb +0 -31
  78. data/lib/lore/behaviours/versioned.rb +0 -36
  79. data/lib/lore/connection.rb +0 -152
  80. data/lib/lore/exception/invalid_klass_parameters.rb +0 -63
  81. data/lib/lore/exception/unknown_typecode.rb +0 -19
  82. data/lib/lore/result.rb +0 -119
  83. data/lib/lore/symbol.rb +0 -58
  84. data/lib/lore/table_accessor.rb +0 -1790
  85. data/lib/lore/table_deleter.rb +0 -116
  86. data/lib/lore/table_inserter.rb +0 -170
  87. data/lib/lore/table_instance.rb +0 -389
  88. data/lib/lore/table_selector.rb +0 -285
  89. data/lib/lore/table_updater.rb +0 -157
  90. data/lib/lore/validation.rb +0 -65
  91. data/lib/lore/validation/message.rb +0 -60
  92. data/lib/lore/validation/reason.rb +0 -52
  93. data/lore_test.log +0 -2366
  94. data/test/README +0 -31
  95. data/test/custom_models.rb +0 -18
  96. data/test/env.rb +0 -5
  97. data/test/prepare.rb +0 -37
  98. data/test/tc_aspect.rb +0 -58
  99. data/test/tc_cache.rb +0 -83
  100. data/test/tc_clause.rb +0 -104
  101. data/test/tc_deep_inheritance.rb +0 -49
  102. data/test/tc_factory.rb +0 -57
  103. data/test/tc_filter.rb +0 -37
  104. data/test/tc_form.rb +0 -32
  105. data/test/tc_model.rb +0 -140
  106. data/test/tc_prepare.rb +0 -44
  107. data/test/tc_refined_query.rb +0 -88
  108. data/test/tc_table_accessor.rb +0 -267
  109. data/test/tc_thread.rb +0 -100
  110. data/test/test_db.sql +0 -400
  111. data/test/test_lore.rb +0 -50
@@ -1,58 +0,0 @@
1
- require('lore/clause')
2
-
3
- class Symbol
4
-
5
- def intern
6
- self
7
- end
8
-
9
- # Do not overload Symbol#==
10
- def eq(other)
11
- Lore::Clause.new(self.to_s)==(other)
12
- end
13
- alias is eq
14
- def >=(other)
15
- Lore::Clause.new(self.to_s)>=(other)
16
- end
17
- def <=(other)
18
- Lore::Clause.new(self.to_s)<=(other)
19
- end
20
- def >(other)
21
- Lore::Clause.new(self.to_s)>(other)
22
- end
23
- def <(other)
24
- Lore::Clause.new(self.to_s)<=(other)
25
- end
26
- def <=>(other)
27
- Lore::Clause.new(self.to_s)<=>(other)
28
- end
29
- def not(other)
30
- Lore::Clause.new(self.to_s)<=>(other)
31
- end
32
- def like(other)
33
- Lore::Clause.new(self.to_s).like(other)
34
- end
35
- def ilike(other)
36
- Lore::Clause.new(self.to_s).ilike(other)
37
- end
38
- def has_element(other)
39
- Lore::Clause.new(self.to_s).has_element(other)
40
- end
41
- def has_element_like(other)
42
- Lore::Clause.new(self.to_s).has_element_like(other)
43
- end
44
- def has_element_ilike(other)
45
- Lore::Clause.new(self.to_s).has_element_ilike(other)
46
- end
47
- def in(other)
48
- Lore::Clause.new(self.to_s).in(other)
49
- end
50
- def not_in(other)
51
- Lore::Clause.new(self.to_s).not_in(other)
52
- end
53
- def between(s,e)
54
- Lore::Clause.new(self.to_s).between(s,e)
55
- end
56
-
57
- end
58
-
@@ -1,1790 +0,0 @@
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 = Lore.logger
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
- if @has_a.nil? then
401
- if @has_a_klasses.nil? then
402
- @has_a = Array.new
403
- else
404
- @has_a_klasses.each { |k|
405
- @has_a << k
406
- }
407
- end
408
- else
409
- return @has_a
410
- end
411
- end
412
- def self.set_has_a(arg) # :nodoc:
413
- @has_a = arg
414
- end
415
- # }}}
416
-
417
- ##########################################################################
418
- # @has_a getters/setters {{{
419
-
420
- def self.get_has_n # :nodoc:
421
- if @has_n.nil? then
422
- if @has_n_klasses.nil? then
423
- @has_n = Array.new
424
- else
425
- @has_n_klasses.each { |k|
426
- @has_n << k
427
- }
428
- end
429
- else
430
- return @has_n
431
- end
432
- end
433
- def self.set_has_n(arg) # :nodoc:
434
- @has_n = arg
435
- end
436
- # }}}
437
-
438
- ##########################################################################
439
- # @is_a getters/setters {{{
440
- # don't collect is_a from ancestors!
441
-
442
- def self.get_is_a # :nodoc:
443
- if @is_a.nil?
444
- Hash.new
445
- else
446
- @is_a
447
- end
448
- end
449
- def self.set_is_a(arg) # :nodoc:
450
- @is_a = arg
451
- end
452
- # }}}
453
-
454
- ##########################################################################
455
- # @aggregates getters/setters {{{
456
- # don't collect aggregates from ancestors!
457
-
458
- def self.get_aggregates # "nodoc:
459
- if @aggregates.nil?
460
- Hash.new
461
- else
462
- @aggregates
463
- end
464
- end
465
- def self.set_aggregates(arg) # :nodoc:
466
- @aggregates = arg
467
- end
468
- # }}}
469
-
470
- def self.get_joins
471
- # {{{
472
- @joins = get_is_a.dup.update(get_aggregates) if @joins.nil?
473
- return @joins
474
- end # def }}}
475
-
476
- ##########################################################################
477
- # @is_a_klasses getters/setters {{{
478
- # don't collect is_a from ancestors!
479
-
480
- def self.get_is_a_klasses # :nodoc:
481
- if @is_a_klasses.nil?
482
- Hash.new
483
- else
484
- @is_a_klasses
485
- end
486
- end
487
- def self.set_is_a_klasses(arg) # :nodoc:
488
- @is_a_klasses = arg
489
- end
490
- # }}}
491
-
492
- ##########################################################################
493
- # @aggregate_klasses getters/setters {{{
494
- # don't collect is_a from ancestors!
495
-
496
- def self.get_aggregate_klasses # :nodoc:
497
- if @aggregate_klasses.nil?
498
- Hash.new
499
- else
500
- @aggregate_klasses
501
- end
502
- end
503
-
504
- def self.set_aggregate_klasses(arg) # :nodoc:
505
- @aggregate_klasses = arg
506
- end
507
- # }}}
508
-
509
- ##########################################################################
510
- # @has_a_klasses getters/setters {{{
511
- # don't collect is_a from ancestors!
512
-
513
- def self.get_has_a_klasses # :nodoc:
514
- if @has_a_klasses.nil?
515
- Hash.new
516
- else
517
- @has_a_klasses
518
- end
519
- end
520
- def self.set_has_a_klasses(arg) # :nodoc:
521
- @has_a_klasses = arg
522
- end
523
- # }}}
524
-
525
- ##########################################################################
526
- # @explicit getters/setters {{{
527
-
528
- def self.get_explicit # :nodoc:
529
- if @explicit.nil?
530
- Hash.new
531
- else
532
- @explicit
533
- end
534
- end # def
535
- def self.set_explicit(arg) # :nodoc:
536
- @explicit = arg
537
- end # def
538
- # }}}
539
-
540
- def self.cache_entities
541
- @cache_entities = true
542
- end
543
- def self.cache_entities?
544
- return false # Temp defunc
545
- @cache_entities
546
- end
547
-
548
- protected
549
-
550
- def self.add_input_filter(attr_name, &block)
551
- @input_filters = Hash.new if @input_filters.nil?
552
- @input_filters[attr_name] = Proc.new &block
553
- end
554
-
555
- def self.add_output_filter(attr_name, &block)
556
- @output_filters = Hash.new if @output_filters.nil?
557
- @output_filters[attr_name] = Proc.new &block
558
- end
559
-
560
- def self.validates(attrib, constraints)
561
- # {{{
562
- if attrib.kind_of? Clause then
563
- attrib_split = attrib.to_s.split('.')
564
- table = attrib_split[0..-2]
565
- attrib = attrib_split[-1]
566
- else
567
- table = get_table_name
568
- end
569
- attrib = attrib.intern unless attrib.instance_of? Symbol
570
-
571
- @constraints = Hash.new if @constraints.nil?
572
- @constraints[table] = Hash.new if @constraints[table].nil?
573
- @constraints[table][attrib] = Hash.new
574
-
575
- if constraints[:mandatory] then
576
- add_explicit_attribute(table, attrib.to_s)
577
- end
578
- if constraints[:type] then
579
- # @attribute_types[table][attrib] = constraints[:type]
580
- @constraints[table][attrib][:type] = constraints[:type]
581
- end
582
- if constraints[:format] then
583
- @constraints[table][attrib][:format] = constraints[:format]
584
- end
585
- if constraints[:length] then
586
- if constraints[:length].kind_of? Range then
587
- @constraints[table][attrib][:minlength] = constraints[:length].first
588
- @constraints[table][attrib][:maxlength] = constraints[:length].last
589
- else
590
- @constraints[table][attrib][:minlength] = constraints[:length]
591
- @constraints[table][attrib][:maxlength] = constraints[:length]
592
- end
593
- end
594
- if constraints[:minlength] then
595
- @constraints[table][attrib][:minlength] = constraints[:minlength]
596
- end
597
- if constraints[:maxlength] then
598
- @constraints[table][attrib][:maxlength] = constraints[:maxlength]
599
- end
600
- end # }}}
601
-
602
- def self.get_maxlength()
603
- end
604
-
605
- private
606
-
607
- ##########################################################################
608
- #
609
- def self.add_foreign_keys(args)
610
- # {{{
611
- other_table = args.at(0).to_s # first index is table name (see usage)
612
-
613
- new_foreign_keys = Hash.new
614
- new_foreign_keys.update(get_foreign_keys)
615
- new_foreign_keys[other_table] = args[1...args.length]
616
-
617
- set_foreign_keys(new_foreign_keys)
618
- end # }}}
619
-
620
- private
621
-
622
- ##########################################################################
623
- # Add single or multiple primary keys to @primary_keys[args.at(0)]:
624
- def self.add_primary_keys(args)
625
- # {{{
626
- other_table = args.at(0) # first index is table name (see usage)
627
-
628
- new_primary_keys = Hash.new
629
- new_primary_keys.update(get_primary_keys)
630
-
631
- foreign_p_keys = args[1...args.length]
632
- foreign_p_keys.collect! {|elem| elem.to_s}
633
-
634
- if(new_primary_keys[other_table].nil?)
635
- new_primary_keys[other_table] = foreign_p_keys
636
- else
637
- new_primary_keys[other_table] += foreign_p_keys
638
- end
639
- @primary_keys = new_primary_keys
640
- end # }}}
641
-
642
- private
643
-
644
- ##########################################################################
645
- # Add one sequence to @sequences[table_name]:
646
- def self.add_sequence(table_name, field_name, sequence_name)
647
- # {{{
648
- new_sequences = Hash.new
649
- new_sequences.update(get_sequences)
650
- new_sequences[table_name] = Hash.new
651
- if sequence_name != nil
652
- new_sequences[table_name][field_name] = sequence_name.to_s
653
- end
654
- set_sequences(new_sequences)
655
- end # }}}
656
-
657
- private
658
-
659
- ##########################################################################
660
- def self.add_attributes(attrib_hash)
661
- # {{{
662
- new_attributes = Hash.new
663
- new_attributes.update(get_attributes)
664
- new_attributes.update(attrib_hash)
665
- set_attributes(new_attributes)
666
- end # }}}
667
-
668
- private
669
-
670
- ##########################################################################
671
- def self.add_attribute_types(attrib_hash)
672
- # {{{
673
- new_types = Hash.new
674
- new_types.update(get_attribute_types)
675
- new_types.update(attrib_hash)
676
- set_attribute_types(new_types)
677
- end # }}}
678
-
679
- private
680
-
681
- ##########################################################################
682
- def self.add_explicit(explicit_hash)
683
- # {{{
684
- new_explicit = explicit_hash
685
- new_explicit.update(get_explicit)
686
- set_explicit(new_explicit)
687
- end # }}}
688
-
689
- private
690
-
691
- ##########################################################################
692
- def self.add_explicit_attributes(attrib_hash)
693
- # {{{
694
- attrib_hash.each_pair { |table, attribs|
695
- if @explicit_attributes and @explicit_attributes[table] then
696
- @explicit_attributes[table] = attrib_hash[table] + @explicit_attributes[table]
697
- else
698
- @explicit_attributes = Hash.new unless @explicit_attributes
699
- @explicit_attributes[table] = attrib_hash[table]
700
- end
701
- }
702
- end # }}}
703
-
704
- private
705
-
706
- ##########################################################################
707
- def self.add_implicit_attributes(attrib_hash)
708
- # {{{
709
- attrib_hash.each_pair { |table, attribs|
710
- if @implicit_attributes and @implicit_attributes[table] then
711
- @implicit_attributes[table] = attrib_hash[table] + @implicit_attributes[table]
712
- else
713
- @implicit_attributes = Hash.new unless @implicit_attributes
714
- @implicit_attributes[table] = attrib_hash[table]
715
- end
716
- }
717
- end # }}}
718
-
719
- protected
720
-
721
- # Demands a value to be set for create and update procedures.
722
- def self.expects(attrib_name, klass=nil)
723
- # {{{
724
- if klass.nil? then table = get_table_name
725
- else table = klass.table end
726
- add_explicit_attribute(table, attrib_name.to_s)
727
- end # }}}
728
-
729
- def self.hide_attribute(attrib_name)
730
- # {{{
731
- @hidden_attributes = Hash.new if @hidden_attributes.nil?
732
- @hidden_attributes[get_table_name] = Array.new if @hidden_attributes[get_table_name].nil?
733
- @hidden_attributes[get_table_name] << attrib_name
734
- end
735
-
736
- def self.get_hidden_attributes()
737
- return @hidden_attributes if @hidden_attributes
738
- Hash.new
739
- end # }}}
740
-
741
- private
742
-
743
- ##########################################################################
744
- #
745
- def self.add_explicit_attribute(table_name, attrib_name)
746
- # {{{
747
- table_explicit_attributes = get_explicit_attributes[table_name]
748
- if(!table_explicit_attributes.nil?)
749
- then table_explicit_attributes.push(attrib_name)
750
- else table_explicit_attributes = [attrib_name]
751
- end
752
-
753
- new_explicit_attributes =
754
- get_explicit_attributes.update({table_name => table_explicit_attributes})
755
-
756
- set_explicit_attributes(new_explicit_attributes)
757
- end # }}}
758
-
759
- private
760
-
761
- def self.add_implicit_attribute(table_name, attrib_name)
762
- # {{{
763
- table_implicit_attributes = get_implicit_attributes[table_name]
764
- if(!table_implicit_attributes.nil?)
765
- then table_implicit_attributes.push(attrib_name)
766
- else table_implicit_attributes = [attrib_name]
767
- end
768
-
769
- new_implicit_attributes =
770
- get_implicit_attributes.update({table_name => table_implicit_attributes})
771
-
772
- set_implicit_attributes(new_implicit_attributes)
773
- end # }}}
774
-
775
- ##########################################################################
776
-
777
- public
778
-
779
- # Returns full attribute name of given attribute
780
- def self.[](attribute_name)
781
- # {{{
782
- return get_table_name+'.'+attribute_name.to_s
783
- end # }}}
784
-
785
- # Constructur is private as class is initialized
786
- # via .create or .load:
787
-
788
- public
789
-
790
- ##########################################################################
791
- # Constructor is usually wrapped by e.g. self.load or
792
- # self.marshal_load.
793
- # Constructor just accepts a value hash, and returns a Table_Accessor
794
- # instance holding it.
795
- # Note that this method is operating on a Table_Accessor instance, not
796
- # on class Table_Accessor itself.
797
- def initialize(instance_attrib_values, joined_models=[], cache=nil)
798
- # {{{
799
- @loaded_from_cache = (cache == :cached)
800
- # set instance variables.
801
- # class instance variables are made accessible
802
- # in Table_Instance.setup_instance
803
- @joined_models = joined_models
804
-
805
- if @loaded_from_cache then
806
- @attribute_values = instance_attrib_values
807
- else
808
- @attribute_values = Hash.new
809
- values = instance_attrib_values
810
- field_index = 0
811
- models = [ self.class ]
812
- models += joined_models
813
- models.each { |model|
814
- model.get_all_table_names.each { |table|
815
- @attribute_values[table] = Hash.new
816
- field_names = model.get_attributes[table]
817
- for attr_index in 0...field_names.length do
818
- @attribute_values[table][field_names[attr_index]] = values[field_index]
819
- field_index += 1
820
- end
821
- }
822
- }
823
- end
824
-
825
- setup_instance()
826
- end # }}}
827
-
828
- public
829
-
830
- ##########################################################################
831
- # If this model is not to be located in a projects default context,
832
- # you can tell Cuba which context to use via
833
- #
834
- # context :other_context_name
835
- #
836
- def self.context(context_name)
837
- # {{{
838
- @context = context_name
839
- end
840
- def self.get_context
841
- @context
842
- end
843
- # }}}
844
-
845
- public
846
-
847
- ##########################################################################
848
- # use table :tablename if class != table only.
849
- def self.table(_table, _schema=nil)
850
- # {{{
851
- set_table_name(_table.to_s)
852
- schema(_schema.to_s) unless _schema.nil?
853
- load_attribute_fields();
854
- end # }}}
855
-
856
- private
857
-
858
- ##########################################################################
859
- # use schema :schemaname if there is a schema only.
860
- # @schema getters/setters {{{
861
- def self.schema(_schema)
862
- set_table_name( _schema.to_s+'.'+get_table_name )
863
- end
864
- def self.get_schema # :nodoc:
865
- @schema
866
- end
867
- # }}}
868
-
869
- protected
870
-
871
- ##########################################################################
872
- # usage in derived classes:
873
- # primary_key :some_field, :some_field_sequence_name
874
- # primary_key :some_other_field, :some_other_field_sequence_name
875
- #
876
- # don't extend primary_keys of parent!
877
- def self.primary_key(*prim_key)
878
- # {{{
879
- keys = Array.new(1,get_table_name) + Array.new(1,prim_key.at(0))
880
- add_primary_keys(keys)
881
-
882
- add_sequence(get_table_name, prim_key.at(0), prim_key.at(1))
883
-
884
- # if sequence is given, this primary key is an implicit attribute:
885
- if(prim_key.at(1).nil?) then
886
- add_explicit_attribute(get_table_name, prim_key.at(0).to_s)
887
- else
888
- add_implicit_attribute(get_table_name, prim_key.at(0).to_s)
889
- end
890
- end # }}}
891
-
892
- # Called in Cuba.import_imp_model. Defines prepared statements like
893
- # The_Model.by_id(id), The_Model.latest_entries(id, amount) etc.
894
- def self.define_default_preps
895
- return
896
- unless @@prepared_statements[:default_preps] then
897
- pkey_attrib_name = table_name + '.' << @primary_keys[table_name].first.to_s
898
- prepare(:_by_id, Lore::Type.integer) { |e|
899
- e.where(self.__send__(pkey_attrib_name.to_s.split('.')[-1].intern) == Lore::Clause.new('$1'))
900
- e.limit(1)
901
- }
902
- prepare(:_latest, Lore::Type.integer) { |e|
903
- e.where(true)
904
- e.order_by(pkey_attrib_name, :desc)
905
- e.limit(Lore::Clause.new('$1'))
906
- }
907
- @@prepared_statements[:default_preps] = true
908
- end
909
- end
910
-
911
- def self.by_id(entity_id)
912
- begin
913
- return _by_id(entity_id).first
914
- rescue ::Exception => excep
915
- if @@prepared_statements[:default_preps] then
916
- raise excep
917
- else
918
- raise ::Exception.new(excep.message + ' (call define_default_preps first?)')
919
- end
920
- end
921
- end
922
-
923
- protected
924
-
925
- ##########################################################################
926
- # usage in derived classes:
927
- # has_a :table_a, :foreign_key_1, foreign_key_2, ...
928
- # has_a :table_b, :foreign_key_1, foreign_key_2, ...
929
- #
930
- # note that *foreign_keys fields in _this_ table,
931
- # not the foreign one.
932
- # we want foreign_keys to look like: hash { 'table_a' => array {'id',
933
- # 'field' }
934
- # 'table_b' => array {'table_b_id'}
935
- # thus allowing key tuples and free naming of table fields.
936
- def self.has_a(*args)
937
- # {{{
938
- if (args.length < 2) then
939
- raise Lore::Exception::Invalid_Usage.new('has_a expects at least Type, :foreign_key_name');
940
- end
941
-
942
- accessor = args.at(0)
943
-
944
- foreign_table_name = accessor.get_table_name
945
- # convert schema.table => table:
946
- foreign_table = foreign_table_name.split('.').at(1)
947
- # convert Module::Other::Klass_Name => klass_name:
948
- foreign_name = accessor.to_s.split('::').at(-1).downcase
949
-
950
- foreign_keys = Array.new(1,foreign_table_name) + args[1..-1]
951
- add_foreign_keys(foreign_keys)
952
-
953
- new_has_a = Array.new(1,foreign_table_name)
954
-
955
- new_has_a = get_has_a + new_has_a
956
- set_has_a(new_has_a)
957
-
958
- add_has_a_klass(accessor, args[1..-1])
959
-
960
- define_entity_access_methods(accessor, args[1..-1])
961
- end # }}}
962
-
963
- def self.maps(*accessors)
964
- @primary_keys = Hash.new
965
- accessors.each { |acc|
966
- acc_pkeys = acc.get_primary_keys
967
- # Define accessor as aggregation. This also defines
968
- # acc's primary keys as own primary keys.
969
- aggregates acc, acc_pkeys
970
- }
971
- end
972
-
973
- private
974
-
975
- def self.define_attribute_clause_methods(accessor)
976
-
977
- if !accessor.get_context.nil? && @context != accessor.get_context then
978
- Lore::Context.enter(accessor.get_context)
979
- context_switched = true
980
- end
981
- if accessor.get_attributes[accessor.get_table_name] then
982
- accessor.get_attributes[accessor.get_table_name].each { |attribute|
983
- # Only define methods on own attributes or on
984
- # attributes only a parent Table_Accessor provides:
985
- if accessor == self || !(get_attributes[get_table_name].include? attribute.to_s) then
986
- attribute_type = accessor.get_attribute_types[accessor.get_table_name][attribute]
987
- # log('Expected attribute type for ' << accessor.get_table_name + '.' << attribute.to_s + ': ' << Lore::Type.type_name(attribute_type).to_s + '(' << attribute_type.to_s + ')' )
988
- method =
989
- "def self.#{attribute}()
990
- Clause.new('#{accessor.get_table_name}.#{attribute}', '', '', { :add_types => [ #{attribute_type}], :add_name => '' })
991
- end"
992
- class_eval(method)
993
-
994
- "def #{attribute}()
995
- attr(:#{attribute})
996
- end"
997
- class_eval(method)
998
-
999
- method =
1000
- "def set_#{attribute}(value)
1001
- set_attribute_value(:#{attribute.to_s}, value)
1002
- end"
1003
- class_eval(method)
1004
-
1005
- method =
1006
- "def #{attribute}=(value)
1007
- set_attribute_value(:#{attribute.to_s}, value)
1008
- end"
1009
- class_eval(method)
1010
-
1011
- end
1012
- }
1013
- end
1014
- Lore::Context.leave if context_switched
1015
-
1016
- end
1017
-
1018
- private
1019
-
1020
- # Meta-programs class instance methods for accessing types
1021
- # associated via has_a.
1022
- def self.define_entity_access_methods(accessor, foreign_keys, type_name=nil)
1023
- type_name = accessor.to_s.split('::').at(-1).downcase unless type_name
1024
-
1025
- log('defining method '+type_name+'_entity')
1026
- define_method(type_name+'_entity') {
1027
-
1028
- has_a_keys = Hash.new
1029
- foreign_keys.each { |foreign_key|
1030
- # self.<foreign_key> will return corresponding value
1031
- # no matter if this klass or a super klass is holding it
1032
- has_a_keys[accessor[foreign_key]] = self.__send__ foreign_key
1033
- }
1034
- return accessor.load(has_a_keys)
1035
- }
1036
-
1037
- log('defining method set_'+type_name+'_entity')
1038
- define_method('set_'+type_name+'_entity') { |other|
1039
- foreign_keys.each { |foreign_key|
1040
- # other.<foreign_key> will return corresponding value
1041
- # no matter if this klass or a super klass is holding it
1042
- self[foreign_key] = other.__send__ foreign_key
1043
- }
1044
- }
1045
- define_method('set_'+type_name+'_entity!') { |other|
1046
- self.__send__ 'set_'+type_name+'_entity', other
1047
- self.commit
1048
- }
1049
- end
1050
-
1051
- private
1052
-
1053
- # Meta-programs class instance methods for accessing types
1054
- # associated via has_n.
1055
- def self.define_entities_access_methods(accessor, values)
1056
- type_name = accessor.to_s.split('::').at(-1).downcase unless type_name
1057
-
1058
- log('defining method add_'+type_name+'_entity')
1059
- define_method('add_'+type_name+'_entity') { |values|
1060
- values.update(get_primary_key_values)
1061
- accessor.create(values)
1062
- }
1063
- log('defining method '+type_name+'_entities')
1064
- define_method(type_name+'_entities') {
1065
- foreign_key_values = Hash.new
1066
-
1067
- accessor.get_foreign_keys[self.table_name].each { |key|
1068
- foreign_key_values[key] = get_attribute_value[key]
1069
- }
1070
- accessor.all_with(foreign_key_values)
1071
- }
1072
- end
1073
-
1074
- protected
1075
-
1076
- def self.belongs_to(*args)
1077
- parent = args[0]
1078
- foreign_table_name = parent.get_table_name
1079
- own_foreign_keys = args[1..-1]
1080
-
1081
- own_foreign_keys = own_foreign_keys.map { |x| x = x.to_s }
1082
- add_explicit_attributes(get_table_name => own_foreign_keys)
1083
-
1084
- args[1..-1].each { |a| hide_attribute(a.to_s) }
1085
- end
1086
-
1087
- ##########################################################################
1088
- # usage in derived classes:
1089
- # has_n :table_a, :foreign_key_1, foreign_key_2, ...
1090
- # has_n :table_b, :foreign_key_1, foreign_key_2, ...
1091
- def self.has_n(*args)
1092
- # {{{
1093
-
1094
- if (args.length < 2) then
1095
- raise Lore::Exception::Invalid_Parameter.new('has_n expects at least :Type, :foreign_key_name');
1096
- end
1097
-
1098
- foreign_table_name = args.at(0).get_table_name
1099
- # convert schema.table => table:
1100
- foreign_table = args.at(0).get_table_name.split('.').at(1)
1101
- # convert Module::Other::Klass_Name => klass_name:
1102
- foreign_name = args.at(0).to_s.split('::')[-1].downcase
1103
-
1104
- foreign_keys = Array.new(1,foreign_table_name) + args[1...args.length]
1105
- add_foreign_keys(foreign_keys)
1106
-
1107
- new_has_n = Array.new(1,foreign_table_name)
1108
- new_has_n = get_has_n + new_has_n
1109
- set_has_n(new_has_n)
1110
-
1111
- method_suffix = foreign_name
1112
-
1113
- log('defining method '+method_suffix+'_entities')
1114
- # TODO: To be wrapped by Table_Instance, feeding this method with
1115
- # primary keys required to get referenced has_n tuples.
1116
- define_method(method_suffix+'_entities') { |*key_values|
1117
-
1118
- query_args = Hash.new
1119
- query_args['_table'] = foreign_table_name
1120
- for arg_counter in 0...key_values.length do
1121
-
1122
- query_args[@foreign_keys[foreign_table_name][arg_counter]] = key_values[arg_counter]
1123
-
1124
- end
1125
- #Table_Selector.perform(query_args)
1126
- }
1127
- # attribs is expected to be a Hash { 'field'=>'value', ... }
1128
- log('defining method add_'+method_suffix)
1129
- # TODO: To be wrapped by Table_Instance, feeding this method with
1130
- # primary keys and attribute values required to add referenced
1131
- # has_n tuples.
1132
- define_method('add_'+method_suffix) {|*attribs|
1133
-
1134
- #query_args = Hash.new
1135
- #query_args['_table'] = foreign_table_name
1136
- #query_args.update(attribs)
1137
-
1138
- #Table_Inserter.perform(query_args)
1139
- }
1140
-
1141
- log('defining method remove_'+method_suffix)
1142
- # TODO: To be wrapped by Table_Instance, feeding this method with
1143
- # primary keys required to remove a referenced has_n tuple.
1144
- define_method('remove_'+method_suffix) {|*keys|
1145
-
1146
- query_args = Hash.new
1147
- query_args['_table'] = foreign_table_name
1148
- for arg_counter in 0...keys.length do
1149
-
1150
- #query_args[@foreign_keys[foreign_table_name][arg_counter]] = keys[arg_counter]
1151
-
1152
- end
1153
- #Table_Deleter.perform(query_args)
1154
- }
1155
-
1156
- end # }}}
1157
-
1158
- protected
1159
-
1160
- ##########################################################################
1161
- # usage in derived classes:
1162
- # is_a Other::Module::Other_Klass
1163
- # is_a Another::Module::Another_Klass
1164
- #
1165
- # Effects:
1166
- # If foo.is_a bar then creating a foo will also create a bar instance.
1167
- # Also, loading a foo will aggregate bar automatically.
1168
- def self.is_a(*args)
1169
- # {{{
1170
-
1171
- parent = args.at(0)
1172
-
1173
- # before: parent_pkeys = Hash { Parent => [id, name] }
1174
- # after: @@primary_keys = Hash { Parent => [id, name]
1175
- # This => [this, key] }
1176
-
1177
- if @input_filters.nil? then
1178
- @input_filters = parent.get_input_filters
1179
- else
1180
- @input_filters.update parent.get_input_filters if parent.get_input_filters
1181
- end
1182
-
1183
- if @output_filters.nil? then
1184
- @output_filters = parent.get_output_filters
1185
- else
1186
- @output_filters.update parent.get_output_filters if parent.get_output_filters
1187
- end
1188
-
1189
- @primary_keys.update(parent.get_primary_keys)
1190
-
1191
- @sequences.update(parent.get_sequences)
1192
- # only add parent's own attributes:
1193
- add_attributes(parent.get_attributes)
1194
- add_attribute_types(parent.get_attribute_types)
1195
-
1196
- add_is_a(parent.get_table_name, parent.get_is_a)
1197
- add_is_a_klass(parent, @table_name.to_s+'.' << args.at(1).to_s)
1198
-
1199
- parent.get_has_a_klasses.each_pair { |foreign_key, klass|
1200
- has_a klass, foreign_key.split('.')[-1]
1201
- }
1202
- set_has_a_klasses(get_has_a_klasses.update(parent.get_has_a_klasses))
1203
-
1204
- add_explicit(parent.get_explicit)
1205
-
1206
- add_explicit_attributes(parent.get_explicit_attributes)
1207
- add_implicit_attributes(parent.get_implicit_attributes)
1208
-
1209
- @foreign_keys = Hash.new if @foreign_keys.nil?
1210
- @foreign_keys.update(parent.get_foreign_keys)
1211
-
1212
- foreign_keys = Array.new(1,parent.get_table_name) + args[1...args.length]
1213
-
1214
- add_foreign_keys(foreign_keys)
1215
-
1216
- get_foreign_keys[parent.get_table_name].each { |attrib|
1217
- # add implicit attribute on *own* table, as it is implicit *here*,
1218
- # not in the foreign table
1219
- add_implicit_attribute(get_table_name, attrib.to_s)
1220
- }
1221
-
1222
- define_attribute_clause_methods(parent)
1223
-
1224
- end # }}}
1225
-
1226
- protected
1227
-
1228
- ##########################################################################
1229
- # usage in derived classes:
1230
- # aggregates Other::Module::Other_Klass
1231
- # aggregates Another::Module::Another_Klass
1232
- #
1233
- # Effects:
1234
- # Unlike is_a, If foo.aggregates bar then creating/deleting a foo will not create/delete
1235
- # a bar instance, but loading a foo will aggregate bar automatically, like
1236
- # is_a.
1237
- def self.aggregates(*args)
1238
- # {{{
1239
- parent = args.at(0)
1240
-
1241
- # before: parent_pkeys = Hash { Parent => [id, name] }
1242
- # after: @@primary_keys = Hash { Parent => [id, name]
1243
- # This => [this, key] }
1244
- @primary_keys.update(parent.get_primary_keys)
1245
-
1246
- # only add parent's own attributes:
1247
- add_attributes(parent.get_attributes)
1248
- add_attribute_types(parent.get_attribute_types)
1249
-
1250
- add_aggregate(parent.get_table_name, parent.get_is_a)
1251
- add_aggregate_klass(parent, args[1..-1])
1252
-
1253
- set_has_a_klasses(get_has_a_klasses.update(parent.get_has_a_klasses))
1254
-
1255
- # Hide aggregated attributes for create, update and form
1256
- # generation:
1257
- add_implicit_attributes(parent.get_attributes)
1258
- add_implicit_attributes(parent.get_implicit_attributes)
1259
-
1260
- foreign_keys = Array.new(1,parent.get_table_name) + args[1..-1]
1261
-
1262
- add_foreign_keys(foreign_keys)
1263
-
1264
- get_foreign_keys[parent.get_table_name].each { |attrib|
1265
- # add implicit attribute on *own* table, as it is implicit *here*,
1266
- # not in the foreign table
1267
- add_implicit_attribute(get_table_name, attrib.to_s)
1268
- }
1269
-
1270
- use_label(parent.get_labels)
1271
-
1272
- define_entity_access_methods(parent, args[1..-1])
1273
- end # }}}
1274
-
1275
- private
1276
-
1277
- def self.add_is_a(table_name, derive_tree)
1278
- # {{{
1279
- new_is_a = Hash.new
1280
- new_is_a[table_name] = derive_tree
1281
- new_is_a.update(get_is_a)
1282
-
1283
- set_is_a(new_is_a)
1284
- end # }}}
1285
-
1286
- private
1287
-
1288
- def self.add_is_a_klass(klass, foreign_key)
1289
- # {{{
1290
- @is_a_klasses = Hash.new if @is_a_klasses.nil?
1291
- @is_a_klasses[foreign_key] = klass
1292
- end # }}}
1293
-
1294
- private
1295
-
1296
- def self.add_aggregate(table_name, derive_tree)
1297
- # {{{
1298
- new_aggregates = Hash.new
1299
- new_aggregates[table_name] = derive_tree
1300
- new_aggregates.update(get_aggregates)
1301
-
1302
- set_aggregates(new_aggregates)
1303
- end # }}}
1304
-
1305
- private
1306
-
1307
- def self.add_aggregate_klass(klass, attributes)
1308
- # {{{
1309
- new_aggregate_klasses = get_aggregate_klasses
1310
- attributes.each { |attribute|
1311
- new_aggregate_klasses[get_table_name+'.' << attribute.to_s] = klass
1312
- }
1313
- set_aggregate_klasses(new_aggregate_klasses)
1314
- end # }}}
1315
-
1316
- private
1317
-
1318
- def self.add_has_a_klass(klass, attributes)
1319
- # {{{
1320
- new_has_a_klasses = get_has_a_klasses
1321
- attributes.each { |attribute|
1322
- new_has_a_klasses[get_table_name+'.' << attribute.to_s] = klass
1323
- }
1324
- set_has_a_klasses(new_has_a_klasses)
1325
- end # }}}
1326
-
1327
- protected
1328
- # Tell Table_Accessor to ignore attribute in update commands
1329
- # unless it has a non-empty value.
1330
- def self.explicit(*args)
1331
- # {{{
1332
- new_explicit = get_explicit
1333
- new_explicit[get_table_name] = args.map!{|elem| elem.to_s}
1334
- add_explicit(new_explicit)
1335
- end # }}}
1336
-
1337
- private
1338
-
1339
- ##########################################################################
1340
- # DELETE ME
1341
- #
1342
- # Usage:
1343
- # Cuba::Admin::Container_Local.select({'primary_key_1'=>'2',
1344
- # 'primary_key_2'=>'38',
1345
- # 'additionally_where_field'=>'73'
1346
- # })
1347
- def self.select_row_by_key(*keys)
1348
- # {{{
1349
- # Aggregate klasses this klass is derived from and
1350
- # those it aggregates explicitly:
1351
- begin
1352
- result = Lore::Table_Selector.select_on_keys(self, keys)
1353
- rescue ::Exception => excep
1354
- raise excep
1355
- raise ::Exception.new('Unable to load instance from DB (Reason: ' << excep.message << ')')
1356
- end
1357
- return result.get_row_with_field_names()
1358
- end # }}}
1359
-
1360
- public
1361
-
1362
- # DELETE ME
1363
- def self.select_by_key(*keys)
1364
- # {{{
1365
- result = Lore::Table_Selector.select_on_keys(self, keys)
1366
- field_array = result.get_rows()
1367
- end # }}}
1368
-
1369
- public
1370
-
1371
- def self.update(&block)
1372
- # {{{
1373
- query_string = Lore::Table_Updater.block_update(self, &block)
1374
- end # def }}}
1375
-
1376
- public
1377
-
1378
- def self.select(clause=nil, &block)
1379
- # {{{
1380
- GC.disable
1381
- if(!clause.nil? && !clause.to_s.include?('*,')) then
1382
- query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
1383
- return Clause.new(query_string[:query])
1384
- else
1385
- what = clause.to_s
1386
- result = Lore::Table_Selector.select_cached(what, self, &block)
1387
- # result = Array.new
1388
- # db_result.get_rows.each { |row|
1389
- # result.push(self.new(self, row))
1390
- # }
1391
- GC.enable
1392
- return result
1393
- end
1394
-
1395
- end # }}}
1396
-
1397
- public
1398
-
1399
- def self.select_query(clause=nil, &block)
1400
- query_string = Lore::Table_Selector.select_query(clause.to_s, self, &block)
1401
- end
1402
-
1403
- # Prepares a query for execution. This offers four advantages:
1404
- # - The query doesn't have to be interpreted by the DB every time
1405
- # - The query call is available via direct method call.
1406
- # - DB validates against types, thus preventing SQL injection
1407
- # - It doesn't Lore require to compose the query string again. This
1408
- # effects the most significant performance gain (Up to 60% execution
1409
- # time in some benchmarks)
1410
- # Usage:
1411
- #
1412
- # Article.prepare(:by_name_and_date, Lore::Type::Integer, Lore::Type::Date) { |a,fields|
1413
- # a.where((Article.article_id == fields[0] &
1414
- # (Article.date == fields[1]))
1415
- # }
1416
- # Article.by_name_and_date('Merry Christmas', '20081224')
1417
- #
1418
- # From the PostgreSQL 7.4 Manual:
1419
- #
1420
- # "In some situations, the query plan produced by for a prepared statement may be
1421
- # inferior to the plan produced if the statement were submitted and executed normally.
1422
- # This is because when the statement is planned and the planner attempts to determine
1423
- # the optimal query plan, the actual values of any parameters specified in the
1424
- # statement are unavailable. PostgreSQL collects statistics on the distribution of
1425
- # data in the table, and can use constant values in a statement to make guesses about
1426
- # the likely result of executing the statement. Since this data is unavailable when
1427
- # planning prepared statements with parameters, the chosen plan may be suboptimal. To
1428
- # examine the query plan PostgreSQL has chosen for a prepared statement, use
1429
- # EXPLAIN EXECUTE. "
1430
- #
1431
- def self.prepare(plan_name, *args, &block)
1432
- # {{{
1433
- log('PREPARE: TRYING CLASS METHOD ' << plan_name.to_s)
1434
- if !@@prepared_statements[plan_name] then
1435
- Table_Selector.prepare(plan_name, self, args, &block)
1436
-
1437
- log('PREPARE: CREATE CLASS METHOD ' << plan_name.to_s)
1438
- instance_eval("
1439
- def #{plan_name.to_s}(*args)
1440
- execute_prepared(:#{plan_name}, args)
1441
- end")
1442
- @@prepared_statements[plan_name] = true
1443
- log('PREPARE: CREATED CLASS METHOD ' << plan_name.to_s)
1444
- end
1445
- end # }}}
1446
-
1447
- def self.execute_prepared(plan_name, *args)
1448
- plan_name = "#{table_name.gsub('.','_')}__#{plan_name.to_s}"
1449
- Table_Selector.select_prepared(plan_name, self, args)
1450
- end
1451
-
1452
- public
1453
-
1454
- # Same as select, but returns
1455
- def self.select_value(what, &block)
1456
- # {{{
1457
- db_result = Lore::Table_Selector.select(what, self, &block)
1458
- row = db_result.get_row
1459
- return row[''] if row
1460
- return {}
1461
- end # }}}
1462
-
1463
- public
1464
-
1465
- def self.select_values(what, &block)
1466
- # {{{
1467
- db_result = Lore::Table_Selector.select(what, self, &block)
1468
- return db_result.get_rows[:values].map { |e|
1469
- e.first
1470
- }
1471
- end # }}}
1472
-
1473
- public
1474
-
1475
- ##########################################################################
1476
- # Wrap explicit select. Example:
1477
- # SomeModule::SomeAccessor.explicit_insert({
1478
- # table_name_A =>
1479
- # {'some_field'=>'2',
1480
- # 'other_field'=>'3'},
1481
- # table_name_A =>
1482
- # {'another_field'=>'5'}
1483
- # })
1484
- # Note that field in 'field'=>'value' is exactly the
1485
- # field name in the table (e.g. table_name_A) it holds.
1486
- def self.explicit_insert(keys)
1487
- # {{{
1488
- Lore::Table_Inserter.perform_insert(self, keys)
1489
- end # }}}
1490
-
1491
-
1492
- private
1493
-
1494
- ##########################################################################
1495
- # Wrap default select. Example:
1496
- # SomeModule::SomeAccessor.insert({
1497
- # 'some_field'=>'2',
1498
- # 'other_field'=>'3',
1499
- # 'another_field'=>'5'
1500
- # })
1501
- # Note that field in 'field'=>'value' is exactly the
1502
- # field name in the table it holds.
1503
- # Table_Accessor.insert basically resolves an explicit
1504
- # hash and passes it to Table_Accessor.explicit_insert.
1505
- def self.insert(keys)
1506
- # {{{
1507
-
1508
- key_hash = Hash.new
1509
-
1510
- attribute_name_array = Array.new
1511
- attribute_table = ''
1512
- get_attributes.each_pair { |table, fields|
1513
-
1514
- key_hash[table] = Hash.new
1515
- keys.each_pair { |attribute, value|
1516
-
1517
- attribute = attribute.to_s
1518
- attribute_name_array = attribute.split('.')
1519
- if attribute_name_array.at(2).nil? then
1520
- attribute_table = nil
1521
- else
1522
- attribute_table = (attribute_name_array.at(0).to_s+'.'+attribute_name_array.at(1).to_s)
1523
- end
1524
-
1525
- if fields.include?(attribute) then
1526
- key_hash[table][attribute] = value unless (value == '' || value.nil?)
1527
-
1528
- elsif !attribute_table.nil? and
1529
- fields.include?(attribute_name_array.at(2)) and
1530
- attribute_table == table then
1531
-
1532
- key_hash[table][attribute_name_array.at(2)] = value unless (value == '' || value.nil?)
1533
- end
1534
- }
1535
-
1536
- }
1537
-
1538
- # sequence values only are known after insert operation,
1539
- # so we have to retreive the complete key_hash back from
1540
- # Table_Inserter.perform_insert:
1541
- key_hash = Lore::Table_Inserter.perform_insert(self, key_hash)
1542
- # key_hash has been extended by sequence_values now, so we
1543
- # return it:
1544
- key_hash
1545
-
1546
- end # }}}
1547
-
1548
- private
1549
-
1550
- def self.distribute_attrib_values(attrib_values)
1551
- # {{{
1552
- values = Hash.new
1553
- # Predefine
1554
- attrib_name_array = Array.new
1555
- # distribute attrib names to tables:
1556
- get_attributes().each_pair { |table, attribs|
1557
- table_values = Hash.new
1558
-
1559
- attrib_name_array = Array.new
1560
- attrib_values.each_pair { |attrib_name, attrib_value|
1561
- attrib_name = attrib_name.to_s
1562
- attrib_name_array = attrib_name.split('.')
1563
-
1564
- if attribs.include? attrib_name then
1565
- table_values[attrib_name] = attrib_value
1566
- elsif attribs.include? attrib_name_array.at(2) and
1567
- attrib_name_array.at(0)+'.'+attrib_name_array.at(1) == table then
1568
- table_values[attrib_name_array.at(2)] = attrib_value
1569
- end
1570
- }
1571
- values[table] = table_values
1572
- }
1573
- values
1574
- end # }}}
1575
-
1576
- public
1577
-
1578
- # Create a shallow instance, that is: An instance with no reference to
1579
- # DB model. Attribute values passed to Table_Accessor.create_shallow are
1580
- # not processed through hooks, filters, and validation.
1581
- # Values are, however, processed through output filters.
1582
- # Usage and result is
1583
- # the same as for Table_Accessor.create, but it only returns
1584
- # an accessor instance, without storing it in the database.
1585
- # To commit a shallow copy to database (and thus process given attribute
1586
- # values through stages mentioned before), call #commit.
1587
- def self.create_shallow(attrib_values)
1588
- before_create(attrib_values)
1589
- values = distribute_attrib_values(attrib_values)
1590
- flat_attribs = []
1591
- get_all_table_names.each { |table|
1592
- get_attributes[table].each { |attrib|
1593
- flat_attribs << (values[table][attrib])
1594
- }
1595
- }
1596
- instance = self.new(flat_attribs)
1597
- end
1598
-
1599
- public
1600
-
1601
- ##########################################################################
1602
- # Returns a new Table_Accessor instance by inserting given attribute
1603
- # values into db and returning an instance for further operations.
1604
- def self.create(attrib_values={})
1605
- # {{{
1606
- before_create(attrib_values)
1607
-
1608
- input_filters = get_input_filters
1609
- attrib_key = ''
1610
- attrib_name = ''
1611
-
1612
- attrib_values.each_pair { |attrib_name, attrib_value|
1613
- if attrib_name.instance_of? Symbol then
1614
- attrib_key = attrib_name
1615
- else
1616
- attrib_key = attrib_name.split('.')[-1].intern
1617
- end
1618
-
1619
- if (input_filters && input_filters[attrib_key]) then
1620
- attrib_values[attrib_name] = input_filters[attrib_key].call(attrib_value)
1621
- end
1622
- }
1623
- after_filters(attrib_values)
1624
-
1625
- values = distribute_attrib_values(attrib_values)
1626
-
1627
- begin
1628
- before_validation(values)
1629
- Lore::Validation::Parameter_Validator.invalid_params(self,
1630
- values)
1631
- rescue Lore::Exception::Invalid_Klass_Parameters => ikp
1632
- # log'n'throw
1633
- ikp.log
1634
- raise ikp
1635
- end
1636
-
1637
- before_insert(attrib_values)
1638
-
1639
- # retreive all final attrib values after insert: (this way, also
1640
- # sequence values are resolved):
1641
- #
1642
- attrib_values = insert(attrib_values)
1643
-
1644
- # This would be a double check, as self.load already filters
1645
- # non-primary key attributes
1646
- select_keys = Hash.new
1647
- if @primary_keys then
1648
- @primary_keys[table_name].each { |key|
1649
- select_keys[table_name + '.' << key] = attrib_values[table_name][key]
1650
- }
1651
- end
1652
-
1653
- obj = self.load(select_keys)
1654
- after_create(obj)
1655
-
1656
- return obj
1657
-
1658
- end # }}}
1659
-
1660
- public
1661
-
1662
- ##########################################################################
1663
- # Return new Table_Accessor instance by loading an existing entry from
1664
- # table if present, or false if no entry has been found.
1665
- # Accepts any combination of :primary_key => 'value'
1666
- # Also allows inherited primary keys.
1667
- def self.load(keys)
1668
- # {{{
1669
-
1670
- before_load(keys)
1671
-
1672
- select_keys = {}
1673
- value = false
1674
- get_primary_keys.each_pair { |table, pkeys|
1675
- pkeys.each { |attrib_name|
1676
- value = keys[table+'.'+attrib_name.to_s] # The more explicit, the better.
1677
- value = keys[attrib_name.intern] if value.nil? # Symbols are supposed to be more frequent than strings
1678
- value = keys[attrib_name.to_s] if value.nil?
1679
- select_keys[table+'.'+attrib_name.to_s] = value unless value.nil?
1680
- }
1681
- }
1682
-
1683
- return false if select_keys.empty?
1684
-
1685
- # We have to perform a select here instead of returning
1686
- # the instance with given attribute values, as this is the
1687
- # only way to retreive attribute values set in the DB via
1688
- # default values, triggers, etc.
1689
- cp = Clause.for(self)
1690
- c = Clause.new
1691
- select_keys.each_pair { |k,v|
1692
- c & (Clause.new(k.to_s.dup) == v.to_s)
1693
- }
1694
-
1695
- instance = self.select { |inst|
1696
- inst.where(c)
1697
- inst.limit(1)
1698
- }.first
1699
-
1700
- return false unless instance
1701
- return instance
1702
-
1703
- end # }}}
1704
-
1705
- public
1706
-
1707
- ##########################################################################
1708
- # Delete this object and use Table_Deleter to delete its entity tuple
1709
- # from database.
1710
- def self.delete(value_keys=nil, &block)
1711
- # {{{
1712
-
1713
- if value_keys then
1714
- before_delete(value_keys)
1715
- Lore::Table_Deleter.perform_delete(self, value_keys)
1716
- after_delete(value_keys)
1717
- else
1718
- Lore::Table_Deleter.block_delete(self, &block)
1719
- end
1720
-
1721
- end # }}}
1722
-
1723
- private
1724
-
1725
- ##########################################################################
1726
- # Send a hollow query to db, thus only field names are returned.
1727
- # This works (and has to work) on empty tables, too.
1728
- #
1729
- # For every attribute, a class method with the attribute's name is defined
1730
- # in order to retreive the absolute field name (schema.table.field) by calling
1731
- # Table_Accessor.field
1732
- def self.load_attribute_fields()
1733
- # {{{
1734
- if !@attributes.nil? then
1735
- return @attributes
1736
- end
1737
-
1738
- @attributes = Hash.new
1739
-
1740
- if(get_table_name != '') then
1741
-
1742
- Lore::Context.enter(@context) unless @context.nil?
1743
- begin
1744
- fields_result = Lore::Connection.perform('SELECT * FROM '+get_table_name+' WHERE false')
1745
- ensure
1746
- Lore::Context.leave unless @context.nil?
1747
- end
1748
-
1749
- own_attributes = Hash.new
1750
- own_attributes[get_table_name] = fields_result.get_field_names()
1751
- own_types = Hash.new
1752
- own_types[get_table_name] = fields_result.get_field_types()
1753
- add_attributes(own_attributes)
1754
- add_attribute_types(own_types)
1755
-
1756
- Lore.log { 'Defined attributes: ' << @attributes.inspect }
1757
-
1758
- define_attribute_clause_methods(self)
1759
-
1760
- end # if
1761
- return @attributes
1762
-
1763
- end # }}}
1764
-
1765
- public
1766
-
1767
- ##########################################################################
1768
- # Simulates inheritance: Delegate missing methods to parent Table_Accessor.
1769
- def self.method_missing(meth)
1770
- if @is_a_klasses then
1771
- @is_a_klasses.each_pair { |foreign_key, k|
1772
- return (k.__send__(meth.to_s)) if k.respond_to? meth
1773
- }
1774
- end
1775
- raise ::Exception.new('Undefined method '<< meth.to_s << ' for ' << self.to_s)
1776
- end
1777
-
1778
- public
1779
-
1780
- ##########################################################################
1781
- # Inspect method
1782
- def self.inspect
1783
- # {{{
1784
- 'Lore::Table_Accessor: ' << self.to_s
1785
- end # }}}
1786
-
1787
- end # class
1788
-
1789
- end # module
1790
-