lore 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE +19 -0
- data/README +74 -0
- data/aspect.rb +80 -0
- data/behaviours/lockable.rb +41 -0
- data/behaviours/movable.rb +54 -0
- data/behaviours/versioned.rb +24 -0
- data/benchmark.rb +193 -0
- data/bits.rb +52 -0
- data/cache/abstract_entity_cache.rb +82 -0
- data/cache/bits.rb +22 -0
- data/cache/cacheable.rb +202 -0
- data/cache/cached_entities.rb +116 -0
- data/cache/file_index.rb +35 -0
- data/cache/mmap_entity_cache.rb +67 -0
- data/clause.rb +528 -0
- data/connection.rb +155 -0
- data/custom_functions.sql +14 -0
- data/exception/ambiguous_attribute.rb +14 -0
- data/exception/cache_exception.rb +30 -0
- data/exception/invalid_klass_parameters.rb +63 -0
- data/exception/invalid_parameter.rb +42 -0
- data/exception/unknown_typecode.rb +19 -0
- data/file_index.sql +56 -0
- data/gui/erb_template.rb +79 -0
- data/gui/erb_template_helpers.rhtml +19 -0
- data/gui/form.rb +314 -0
- data/gui/form_element.rb +676 -0
- data/gui/form_generator.rb +151 -0
- data/gui/templates/button.rhtml +2 -0
- data/gui/templates/checkbox.rhtml +3 -0
- data/gui/templates/checkbox_row.rhtml +1 -0
- data/gui/templates/file.rhtml +2 -0
- data/gui/templates/file_readonly.rhtml +3 -0
- data/gui/templates/form_element.rhtml +5 -0
- data/gui/templates/form_element_horizontal.rhtml +3 -0
- data/gui/templates/form_element_listed.rhtml +8 -0
- data/gui/templates/form_table.rhtml +3 -0
- data/gui/templates/form_table_blank.rhtml +3 -0
- data/gui/templates/form_table_horizontal.rhtml +8 -0
- data/gui/templates/password.rhtml +2 -0
- data/gui/templates/password_readonly.rhtml +3 -0
- data/gui/templates/radio.rhtml +1 -0
- data/gui/templates/radio_row.rhtml +1 -0
- data/gui/templates/select.rhtml +23 -0
- data/gui/templates/text.rhtml +2 -0
- data/gui/templates/text_readonly.rhtml +3 -0
- data/gui/templates/textarea.rhtml +3 -0
- data/gui/templates/textarea_readonly.rhtml +4 -0
- data/lore.gemspec +40 -0
- data/lore.rb +94 -0
- data/migration.rb +48 -0
- data/model.rb +139 -0
- data/model_factory.rb +202 -0
- data/model_shortcuts.rb +16 -0
- data/query_shortcuts.rb +367 -0
- data/reserved_methods.txt +3 -0
- data/result.rb +100 -0
- data/symbol.rb +58 -0
- data/table_accessor.rb +1926 -0
- data/table_deleter.rb +115 -0
- data/table_inserter.rb +168 -0
- data/table_instance.rb +384 -0
- data/table_selector.rb +314 -0
- data/table_updater.rb +155 -0
- data/test/README +31 -0
- data/test/env.rb +5 -0
- data/test/lore_test.log +8218 -0
- data/test/model.rb +142 -0
- data/test/prepare.rb +37 -0
- data/test/tc_aspect.rb +58 -0
- data/test/tc_cache.rb +80 -0
- data/test/tc_clause.rb +104 -0
- data/test/tc_deep_inheritance.rb +49 -0
- data/test/tc_factory.rb +57 -0
- data/test/tc_filter.rb +37 -0
- data/test/tc_form.rb +32 -0
- data/test/tc_model.rb +86 -0
- data/test/tc_prepare.rb +45 -0
- data/test/tc_refined_query.rb +88 -0
- data/test/tc_table_accessor.rb +265 -0
- data/test/test.log +181 -0
- data/test/test_db.sql +400 -0
- data/test/ts_lore.rb +49 -0
- data/types.rb +55 -0
- data/validation/message.rb +60 -0
- data/validation/parameter_validator.rb +104 -0
- data/validation/reason.rb +54 -0
- data/validation/type_validator.rb +91 -0
- data/validation.rb +65 -0
- metadata +170 -0
data/table_deleter.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
|
2
|
+
require('lore/connection')
|
3
|
+
require('lore/bits')
|
4
|
+
|
5
|
+
module Lore
|
6
|
+
|
7
|
+
module Table_Deleter # :nodoc:
|
8
|
+
|
9
|
+
@logger = Lore.logger
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def self.atomic_delete_query(table_name, primary_keys, value_keys)
|
14
|
+
|
15
|
+
query_string = 'DELETE FROM '+table_name+' WHERE '
|
16
|
+
|
17
|
+
field_counter=0
|
18
|
+
primary_keys.each { |field|
|
19
|
+
|
20
|
+
query_string << field + '=\''
|
21
|
+
internal_attribute_name = field[0..27]
|
22
|
+
value = value_keys[internal_attribute_name].to_s
|
23
|
+
if value == '' then
|
24
|
+
value = value_keys[table_name+'.'+internal_attribute_name].to_s
|
25
|
+
end
|
26
|
+
|
27
|
+
# attrib = Lore.resolve_passed_value(value_keys, table_name, field)
|
28
|
+
# value = attrib[:value]
|
29
|
+
# internal_attribute_name = attrib[:field]
|
30
|
+
query_string << value + '\' '
|
31
|
+
if field_counter < primary_keys.length-1
|
32
|
+
query_string += 'AND '
|
33
|
+
end
|
34
|
+
field_counter += 1
|
35
|
+
}
|
36
|
+
query_string += ';'
|
37
|
+
|
38
|
+
query_string
|
39
|
+
|
40
|
+
end #def
|
41
|
+
|
42
|
+
def self.block_delete(accessor,
|
43
|
+
&block)
|
44
|
+
|
45
|
+
query_string = 'DELETE FROM '+accessor.get_table_name
|
46
|
+
|
47
|
+
if block_given? then
|
48
|
+
yield_obj = Lore::Clause_Parser.new(accessor.table_name)
|
49
|
+
clause = yield *yield_obj
|
50
|
+
end
|
51
|
+
|
52
|
+
query_string += clause.where_part
|
53
|
+
|
54
|
+
Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
55
|
+
begin
|
56
|
+
Lore::Connection.perform(query_string)
|
57
|
+
ensure
|
58
|
+
Context.leave unless accessor.get_context.nil?
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.delete_query(table_name, is_a_hierarchy, primary_keys, value_keys, query_string='')
|
64
|
+
|
65
|
+
is_a_hierarchy.each_pair { |table, base_tables|
|
66
|
+
|
67
|
+
# pass base tables first, recursively, as IS_A-based creation has
|
68
|
+
# to be done bottom-up:
|
69
|
+
query_string += delete_query(table,
|
70
|
+
base_tables,
|
71
|
+
|
72
|
+
primary_keys,
|
73
|
+
value_keys,
|
74
|
+
|
75
|
+
query_string
|
76
|
+
).to_s
|
77
|
+
}
|
78
|
+
# finally, add query string for this table:
|
79
|
+
query_string += atomic_delete_query(table_name,
|
80
|
+
primary_keys[table_name],
|
81
|
+
value_keys[table_name]
|
82
|
+
).to_s
|
83
|
+
|
84
|
+
query_string
|
85
|
+
|
86
|
+
end # def
|
87
|
+
|
88
|
+
public
|
89
|
+
|
90
|
+
|
91
|
+
|
92
|
+
protected
|
93
|
+
|
94
|
+
def self.perform_delete(accessor,
|
95
|
+
value_keys)
|
96
|
+
|
97
|
+
query_string = delete_query(accessor.get_table_name,
|
98
|
+
accessor.get_is_a,
|
99
|
+
accessor.get_primary_keys,
|
100
|
+
value_keys)
|
101
|
+
|
102
|
+
Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
103
|
+
begin
|
104
|
+
Lore::Connection.perform("BEGIN;\n#{query_string}\nCOMMIT;")
|
105
|
+
ensure
|
106
|
+
Context.leave unless accessor.get_context.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
accessor.flush_entity_cache()
|
110
|
+
|
111
|
+
end #def
|
112
|
+
|
113
|
+
end # module
|
114
|
+
end # module
|
115
|
+
|
data/table_inserter.rb
ADDED
@@ -0,0 +1,168 @@
|
|
1
|
+
|
2
|
+
require('lore/connection')
|
3
|
+
|
4
|
+
module Lore
|
5
|
+
|
6
|
+
module Table_Inserter # :nodoc:
|
7
|
+
|
8
|
+
@logger = Lore.logger
|
9
|
+
|
10
|
+
protected
|
11
|
+
|
12
|
+
def self.load_sequence_values(sequences)
|
13
|
+
|
14
|
+
sequence_values = Hash.new
|
15
|
+
|
16
|
+
sequences.each_pair { |table_name, field|
|
17
|
+
field.each_pair { |field_name, sequence_name|
|
18
|
+
|
19
|
+
pure_schema_name = table_name.split('.')[0]
|
20
|
+
pure_table_name = table_name.split('.')[1]
|
21
|
+
temp_sequence_name = sequence_name+'_temp'
|
22
|
+
|
23
|
+
sequence_query_string = 'SELECT nextval(\''+pure_schema_name+'.'+sequence_name+'\'::text) as '+temp_sequence_name+'; '
|
24
|
+
sequence_value_result = Lore::Connection.perform(sequence_query_string)
|
25
|
+
|
26
|
+
sequence_values[table_name] = Hash.new if sequence_values[table_name] == nil
|
27
|
+
sequence_values[table_name][field_name.to_s] = sequence_value_result.get_field_value(0, temp_sequence_name)
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
return sequence_values
|
32
|
+
|
33
|
+
end
|
34
|
+
|
35
|
+
def self.atomic_insert_query(table_name, value_keys)
|
36
|
+
|
37
|
+
query_string = "\n"
|
38
|
+
query_string += 'INSERT INTO '+table_name
|
39
|
+
|
40
|
+
value_string = String.new
|
41
|
+
field_string = String.new
|
42
|
+
key_counter = 0
|
43
|
+
value_keys.each_pair { |field, value|
|
44
|
+
|
45
|
+
field_string += field
|
46
|
+
value_string += '\''+value.to_s+'\''
|
47
|
+
if key_counter < value_keys.length-1
|
48
|
+
field_string += ', '
|
49
|
+
value_string += ', '
|
50
|
+
end
|
51
|
+
key_counter += 1
|
52
|
+
}
|
53
|
+
query_string += '('+field_string+') VALUES '
|
54
|
+
query_string += '('+value_string+'); '
|
55
|
+
|
56
|
+
query_string
|
57
|
+
|
58
|
+
end #def
|
59
|
+
|
60
|
+
def self.insert_query(table_name,
|
61
|
+
is_a_hierarchy,
|
62
|
+
sequence_values,
|
63
|
+
value_keys,
|
64
|
+
query_string='')
|
65
|
+
|
66
|
+
is_a_hierarchy.each_pair { |table, base_tables|
|
67
|
+
|
68
|
+
# pass base tables first, recursively, as IS_A-based creation has
|
69
|
+
# to be done bottom-up:
|
70
|
+
query_string += insert_query(table, base_tables, sequence_values, value_keys).to_s
|
71
|
+
}
|
72
|
+
# finally, add query string for this table:
|
73
|
+
if(value_keys[table_name] != nil)
|
74
|
+
query_string += atomic_insert_query(table_name, value_keys[table_name]).to_s
|
75
|
+
else
|
76
|
+
@logger.debug { "No initial attribute for IS_A related table #{table_name} given. " }
|
77
|
+
end
|
78
|
+
|
79
|
+
query_string
|
80
|
+
|
81
|
+
end # def
|
82
|
+
|
83
|
+
def self.update_sequence_values_deps( table_names,
|
84
|
+
is_a_hierarchy,
|
85
|
+
foreign_keys,
|
86
|
+
sequence_values,
|
87
|
+
value_keys,
|
88
|
+
allowed_attributes)
|
89
|
+
|
90
|
+
is_a_hierarchy.each_pair { |table, basetables|
|
91
|
+
table_names.each { |table_name|
|
92
|
+
# extend each value_key with primary keys of its base table:
|
93
|
+
if sequence_values.has_key?(table) then
|
94
|
+
# We only want a inherited sequence value update if this table (table_name)
|
95
|
+
# has a field for it at all:
|
96
|
+
sequence_values[table].each_pair { |attrib, seq_val|
|
97
|
+
# Set own foreign keys to value of primary key in
|
98
|
+
# referenced table.
|
99
|
+
# Example: public.car.*vehicle_id* => public.vehicle.id
|
100
|
+
# (Note that own and foreign attribute names differ)
|
101
|
+
if foreign_keys[table].first && allowed_attributes[table_name].include?(foreign_keys[table].first.to_s) then
|
102
|
+
# puts 'setting seq val: ' << table_name.to_s + '.' << foreign_keys[table].first.to_s + ' = ' << seq_val
|
103
|
+
value_keys[table_name][foreign_keys[table].first.to_s] = seq_val
|
104
|
+
end
|
105
|
+
}
|
106
|
+
end
|
107
|
+
}
|
108
|
+
table_names << table
|
109
|
+
value_keys = update_sequence_values_deps(table_names, basetables, foreign_keys, sequence_values, value_keys, allowed_attributes)
|
110
|
+
}
|
111
|
+
|
112
|
+
return value_keys
|
113
|
+
|
114
|
+
end #def
|
115
|
+
|
116
|
+
def self.perform_insert(accessor, value_keys)
|
117
|
+
|
118
|
+
table_name = accessor.get_table_name
|
119
|
+
is_a_hierarchy = accessor.get_is_a.dup
|
120
|
+
foreign_keys = accessor.get_foreign_keys
|
121
|
+
sequences = accessor.get_sequences
|
122
|
+
|
123
|
+
accessor.get_aggregates.each_pair { |table, subtable|
|
124
|
+
is_a_hierarchy.delete(table)
|
125
|
+
}
|
126
|
+
|
127
|
+
Context.enter(accessor.get_context) unless accessor.get_context.nil?
|
128
|
+
@logger.debug('PERFORM INSERT on '+table_name)
|
129
|
+
|
130
|
+
sequence_values = load_sequence_values(sequences)
|
131
|
+
|
132
|
+
# Parse sequence_values into value_keys where entries match the exact table:
|
133
|
+
# (thus, there is an explicit call like primary_key :this_field, :this_sequence)
|
134
|
+
sequence_values.each_pair { |table, fields|
|
135
|
+
|
136
|
+
# TODO: Support different attribute names here, like:
|
137
|
+
# value_keys[table_name][my_field_name] = Hash.new(sequence_values[table][foreign_field_name]
|
138
|
+
# Currently it's
|
139
|
+
# value_keys[table_name][foreign_field_name] = Hash.new(sequence_values[table][foreign_field_name]
|
140
|
+
value_keys[table].update(sequence_values[table])
|
141
|
+
}
|
142
|
+
# Parse sequence_values into value_keys where a table depends from a
|
143
|
+
# sequence field by IS_A
|
144
|
+
value_keys = update_sequence_values_deps([table_name],
|
145
|
+
is_a_hierarchy,
|
146
|
+
foreign_keys,
|
147
|
+
sequence_values,
|
148
|
+
value_keys,
|
149
|
+
accessor.get_attributes)
|
150
|
+
query_string = insert_query(table_name,
|
151
|
+
is_a_hierarchy,
|
152
|
+
sequence_values,
|
153
|
+
value_keys)
|
154
|
+
|
155
|
+
begin
|
156
|
+
Lore::Connection.perform("BEGIN;\n#{query_string}\nCOMMIT;")
|
157
|
+
ensure
|
158
|
+
Lore::Context.leave unless accessor.get_context.nil?
|
159
|
+
end
|
160
|
+
|
161
|
+
accessor.flush_entity_cache()
|
162
|
+
|
163
|
+
# value_keys now are extended by sequence values:
|
164
|
+
return value_keys
|
165
|
+
end # def
|
166
|
+
|
167
|
+
end # module
|
168
|
+
end # module
|
data/table_instance.rb
ADDED
@@ -0,0 +1,384 @@
|
|
1
|
+
|
2
|
+
require('lore/exception/ambiguous_attribute')
|
3
|
+
require('lore/behaviours/movable')
|
4
|
+
require('lore/behaviours/versioned')
|
5
|
+
require('lore/behaviours/lockable')
|
6
|
+
|
7
|
+
module Lore
|
8
|
+
|
9
|
+
class Attribute_Hash < Hash # :nodoc:
|
10
|
+
|
11
|
+
alias random_access_op []
|
12
|
+
alias random_access_assign_op []=
|
13
|
+
def [](key)
|
14
|
+
if !random_access_op(key).nil? then
|
15
|
+
return random_access_op(key)
|
16
|
+
elsif !random_access_op(key.to_s).nil? then
|
17
|
+
return random_access_op(key.to_s)
|
18
|
+
else
|
19
|
+
skey = key.to_s[0..24].intern
|
20
|
+
return random_access_op(skey)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def []=(key, value)
|
25
|
+
begin
|
26
|
+
key = key.to_s[0..24].intern
|
27
|
+
rescue ::Exception => excep
|
28
|
+
Lore.logger.debug { 'Error when trying to access key ' << key.inspect }
|
29
|
+
raise excep
|
30
|
+
end
|
31
|
+
self.random_access_assign_op(key, value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def method_missing(key)
|
35
|
+
self[key]
|
36
|
+
end
|
37
|
+
|
38
|
+
end # class
|
39
|
+
|
40
|
+
# Used as mixin for Table_Accessor.
|
41
|
+
# This module holds methods provided by Table_Accessor instances.
|
42
|
+
module Table_Instance
|
43
|
+
|
44
|
+
|
45
|
+
# retreive class instance variables from concrete
|
46
|
+
# Table_Accessor this object is an instance of:
|
47
|
+
def setup_instance() # :nodoc:
|
48
|
+
|
49
|
+
@touched = false
|
50
|
+
|
51
|
+
@primary_key_values = Hash.new
|
52
|
+
self.class.get_primary_keys.each_pair { |table, attrib_array|
|
53
|
+
@primary_key_values[table] = Hash.new
|
54
|
+
attrib_array.each { |field|
|
55
|
+
begin
|
56
|
+
pk_attrib_value = @attribute_values[table][field]
|
57
|
+
# NOTE: Field length in Postgres is limited to 25 characters normally.
|
58
|
+
pk_attrib_value = @attribute_values[table][field.to_s[0..24]] unless pk_attrib_value
|
59
|
+
@primary_key_values[table][field] = pk_attrib_value
|
60
|
+
rescue ::Exception => excep
|
61
|
+
# We end here if a coder tried to hack a query. We let him
|
62
|
+
# continue in his plans, it might make sense after all.
|
63
|
+
end
|
64
|
+
}
|
65
|
+
}
|
66
|
+
|
67
|
+
# Applying filter to *all* attribute values, including derived attributes.
|
68
|
+
# This way, an output filter can be added in a derived type that does not
|
69
|
+
# exist in a base type.
|
70
|
+
output_filters = self.class.get_output_filters
|
71
|
+
if output_filters then
|
72
|
+
@attribute_values.each_pair { |table,keys|
|
73
|
+
keys.each_pair { |key, value|
|
74
|
+
@attribute_values[table][key] = output_filters[key.intern].call(value) if output_filters[key.intern]
|
75
|
+
}
|
76
|
+
}
|
77
|
+
end
|
78
|
+
|
79
|
+
end # def
|
80
|
+
|
81
|
+
def table_accessor
|
82
|
+
self.class
|
83
|
+
end
|
84
|
+
|
85
|
+
# Create a marshalled dump of this model instance.
|
86
|
+
# Returns attribute values as The only difference
|
87
|
+
# between model instances is their value set.
|
88
|
+
def marshal_dump
|
89
|
+
{
|
90
|
+
:klass => self.class.to_s,
|
91
|
+
:values => get_attribute_values
|
92
|
+
}
|
93
|
+
end
|
94
|
+
|
95
|
+
# Returns primary key values of own table
|
96
|
+
def key
|
97
|
+
@primary_key_values[table_accessor.get_table_name]
|
98
|
+
end
|
99
|
+
|
100
|
+
def get_label_string
|
101
|
+
value = ''
|
102
|
+
Lore.logger.debug { 'LABEL ATTRIBS: ' << @attribute_values.inspect }
|
103
|
+
table_accessor.get_labels.each { |label_attrib|
|
104
|
+
Lore.logger.debug { 'LABEL_STRING: ' << label_attrib }
|
105
|
+
label_parts = label_attrib.split('.')
|
106
|
+
value << @attribute_values[label_parts[0..1].join('.')][label_parts[2]].to_s + ' '
|
107
|
+
}
|
108
|
+
value = '[no label given]' if value == ''
|
109
|
+
return value
|
110
|
+
end
|
111
|
+
|
112
|
+
# Creates an instance of self from marshalled value set.
|
113
|
+
def marshal_load(dump)
|
114
|
+
klass = eval(dump[:klass])
|
115
|
+
return initialize(dump[:values], :cached)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Whether this instance has been loaded from
|
119
|
+
# cache or live from DB.
|
120
|
+
def is_cached_entity?
|
121
|
+
# Set in initialize via marshal_load
|
122
|
+
@loaded_from_cache
|
123
|
+
end
|
124
|
+
|
125
|
+
def touched?
|
126
|
+
@touched
|
127
|
+
end
|
128
|
+
|
129
|
+
def method_missing(meth)
|
130
|
+
begin
|
131
|
+
return attr[meth]
|
132
|
+
rescue ::Exception => excep
|
133
|
+
raise excep
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def move(sortpos, criteria)
|
138
|
+
Lore::Movable.move(self, table_accessor.get_order_attr, sortpos, criteria)
|
139
|
+
end # def
|
140
|
+
|
141
|
+
# Set value for given attribute, e.g. for later commit.
|
142
|
+
# It is recommended to use random access assignment instead:
|
143
|
+
#
|
144
|
+
# instance.set_attribute_value(:name, 'Wombat')
|
145
|
+
# is same as
|
146
|
+
# instance[:name] = 'Wombat'
|
147
|
+
#
|
148
|
+
def set_attribute_value(attrib_name, attrib_value)
|
149
|
+
# {{{
|
150
|
+
|
151
|
+
if @input_filters && (@input_filters.has_key?(attrib_name.intern)) then
|
152
|
+
attrib_value = @input_filters[attrib_name.intern].call(attrib_value)
|
153
|
+
end
|
154
|
+
|
155
|
+
@touched = true
|
156
|
+
# Delete cached value of @flat_attr:
|
157
|
+
@flat_attr = nil
|
158
|
+
save_attribute_values = @attribute_values.dup
|
159
|
+
attrib_name = attrib_name.to_s
|
160
|
+
attrib_name_array = attrib_name.split('.')
|
161
|
+
|
162
|
+
# Attrib name is implicit (no table name given).
|
163
|
+
# Check for ambiguous attribute name:
|
164
|
+
if attrib_name_array[2].nil? then
|
165
|
+
changed_table = false
|
166
|
+
@attribute_values.each { |table, attributes|
|
167
|
+
|
168
|
+
if attributes.has_key?(attrib_name) then
|
169
|
+
@attribute_values[table][attrib_name] = attrib_value
|
170
|
+
changed_table = true
|
171
|
+
elsif attributes.has_key?(attrib_name) && changed_table then
|
172
|
+
raise Lore::Exception::Ambiguous_Attribute.new(table,
|
173
|
+
changed_table,
|
174
|
+
attrib_name)
|
175
|
+
end
|
176
|
+
}
|
177
|
+
|
178
|
+
# Attrib name is explicit (also includes table name).
|
179
|
+
# No need to check for ambiguous attribute:
|
180
|
+
else
|
181
|
+
attrib_name_index = attrib_name_array[2]
|
182
|
+
attrib_table = attrib_name_array[0]+'.'+attrib_name_array[1]
|
183
|
+
|
184
|
+
@attribute_values[attrib_table][attrib_name_index] = attrib_value
|
185
|
+
end
|
186
|
+
|
187
|
+
begin
|
188
|
+
|
189
|
+
# Lore::Validation::Parameter_Validator.invalid_params(
|
190
|
+
# self.class,
|
191
|
+
# @attribute_values)
|
192
|
+
|
193
|
+
rescue Lore::Exception::Invalid_Klass_Parameters => ikp
|
194
|
+
|
195
|
+
# load saved (stable, validated) attribute_values:
|
196
|
+
@attribute_values = save_attribute_values
|
197
|
+
# log'n'throw:
|
198
|
+
ikp.log
|
199
|
+
raise ikp
|
200
|
+
|
201
|
+
rescue Lore::Exception::Ambiguous_Attribute => aa
|
202
|
+
|
203
|
+
# load saved (stable, validated) attribute_values:
|
204
|
+
@attribute_values = save_attribute_values
|
205
|
+
# log'n'throw:
|
206
|
+
raise aa
|
207
|
+
|
208
|
+
end
|
209
|
+
|
210
|
+
end # def }}}
|
211
|
+
|
212
|
+
# Sets attribute value. Example:
|
213
|
+
# instance[:name] = 'Wombat'
|
214
|
+
# instance.commit
|
215
|
+
def []=(key, value)
|
216
|
+
set_attribute_value(key, value)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Explicit attribute request.
|
220
|
+
# Example:
|
221
|
+
# Car[Vehicle.name]
|
222
|
+
# In case name is attribute field in Car and Vehicle.
|
223
|
+
def [](clause)
|
224
|
+
abs_attr(clause)
|
225
|
+
end
|
226
|
+
|
227
|
+
# Returns true if instance points to same records as other instance.
|
228
|
+
# Only compares primary key values.
|
229
|
+
def ==(other)
|
230
|
+
result = true
|
231
|
+
return false if self.class.to_s != other.class.to_s
|
232
|
+
return id() == other.id()
|
233
|
+
end
|
234
|
+
|
235
|
+
# Return primary key value. In case primary key is composed, return it as array.
|
236
|
+
def id
|
237
|
+
key = []
|
238
|
+
table = table_accessor.table_name
|
239
|
+
table_accessor.get_primary_keys[table].each { |attrib_array|
|
240
|
+
attrib_array.each { |field|
|
241
|
+
key << get_attribute_values[table][field]
|
242
|
+
}
|
243
|
+
}
|
244
|
+
return key.first if key.length < 2
|
245
|
+
return key
|
246
|
+
end
|
247
|
+
|
248
|
+
# Returns true if instance points to same records as other instance,
|
249
|
+
# also compares non-key attribute values.
|
250
|
+
def ===(other)
|
251
|
+
return false unless (self == other)
|
252
|
+
|
253
|
+
end
|
254
|
+
# See ==
|
255
|
+
def <=>(other)
|
256
|
+
return !(self.==(other))
|
257
|
+
end
|
258
|
+
|
259
|
+
# Returns all attribute values as hash.
|
260
|
+
def get_attribute_values() # :nodoc:
|
261
|
+
@attribute_values
|
262
|
+
end # def
|
263
|
+
|
264
|
+
# Returns value hash of instance attributes like:
|
265
|
+
#
|
266
|
+
# {
|
267
|
+
# 'schema.table.id' => 123,
|
268
|
+
# 'schema.atable.name' => 'example'
|
269
|
+
# }
|
270
|
+
#
|
271
|
+
# Common usage:
|
272
|
+
#
|
273
|
+
# table_instance.attr[:id] -> 123
|
274
|
+
#
|
275
|
+
# But it is recommended to use
|
276
|
+
#
|
277
|
+
# table_instance.id -> 123
|
278
|
+
#
|
279
|
+
def attr
|
280
|
+
|
281
|
+
if @flat_attr.nil? then
|
282
|
+
@flat_attr = Attribute_Hash.new
|
283
|
+
get_attribute_values.each_pair { |table, attribs|
|
284
|
+
attribs.each_pair { |attrib_name, value|
|
285
|
+
@flat_attr[attrib_name] = value unless value.nil?
|
286
|
+
}
|
287
|
+
}
|
288
|
+
end
|
289
|
+
@flat_attr
|
290
|
+
|
291
|
+
end # def
|
292
|
+
|
293
|
+
# Returns value hash of instance attributes
|
294
|
+
# of a given subtype like:
|
295
|
+
#
|
296
|
+
# {
|
297
|
+
# 'id' => 123,
|
298
|
+
# 'name' => 'example'
|
299
|
+
# }
|
300
|
+
#
|
301
|
+
# Common usage:
|
302
|
+
#
|
303
|
+
# table_accessor.abs_attr(Klass_A)[:id] -> 123
|
304
|
+
#
|
305
|
+
def abs_attr(klass=nil)
|
306
|
+
klass = klass.to_s if klass.instance_of? Symbol
|
307
|
+
return get_attribute_values if klass.nil?
|
308
|
+
return get_attribute_values[klass.table_name] if klass.kind_of? Lore::Table_Accessor
|
309
|
+
return get_attribute_values[klass] if klass.instance_of? String
|
310
|
+
return get_attribute_values[klass.to_s.split('.')[0..1].join('.').to_s][klass.to_s.split('.').at(-1)] if klass.instance_of? Lore::Clause
|
311
|
+
|
312
|
+
end # def
|
313
|
+
|
314
|
+
def get_primary_key_values
|
315
|
+
@primary_key_values
|
316
|
+
end
|
317
|
+
|
318
|
+
|
319
|
+
# Commit changes on Table_Accessor instance to DB.
|
320
|
+
# Results in an / several SQL update calls.
|
321
|
+
#
|
322
|
+
# Common usage:
|
323
|
+
#
|
324
|
+
# unit['name'] = 'changed'
|
325
|
+
# unit.commit()
|
326
|
+
#
|
327
|
+
def commit
|
328
|
+
# {{{
|
329
|
+
|
330
|
+
return unless @touched
|
331
|
+
|
332
|
+
input_filters = self.class.get_input_filters
|
333
|
+
if input_filters then
|
334
|
+
@attribute_values.each_pair { |table,keys|
|
335
|
+
keys.each_pair { |key, value|
|
336
|
+
@attribute_values[table][key] = input_filters[key.intern].call(value) if input_filters[key.intern]
|
337
|
+
}
|
338
|
+
}
|
339
|
+
end
|
340
|
+
|
341
|
+
begin
|
342
|
+
Lore::Validation::Parameter_Validator.invalid_params(self.class,
|
343
|
+
@attribute_values)
|
344
|
+
rescue Lore::Exception::Invalid_Klass_Parameters => ikp
|
345
|
+
# log'n'throw:
|
346
|
+
ikp.log
|
347
|
+
raise ikp
|
348
|
+
end
|
349
|
+
|
350
|
+
table_accessor.before_commit(self)
|
351
|
+
|
352
|
+
Table_Updater.perform_update(table_accessor,
|
353
|
+
self)
|
354
|
+
|
355
|
+
table_accessor.after_commit(self)
|
356
|
+
|
357
|
+
@touched = false
|
358
|
+
|
359
|
+
end # def }}}
|
360
|
+
alias save commit
|
361
|
+
|
362
|
+
# Delete this instance from DB.
|
363
|
+
# Common usage:
|
364
|
+
#
|
365
|
+
# unit = Some_Table_Accessor.select { ... }.first
|
366
|
+
# unit.delete
|
367
|
+
#
|
368
|
+
#
|
369
|
+
def delete
|
370
|
+
|
371
|
+
# Called before entity_instance.delete
|
372
|
+
table_accessor.before_instance_delete(self)
|
373
|
+
|
374
|
+
Table_Deleter.perform_delete(table_accessor,
|
375
|
+
@attribute_values)
|
376
|
+
# Called after entity_instance.delete
|
377
|
+
table_accessor.after_instance_delete(self)
|
378
|
+
|
379
|
+
end # def
|
380
|
+
|
381
|
+
end # module
|
382
|
+
|
383
|
+
|
384
|
+
end # module
|