lore 0.4.8 → 0.9.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.
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
-