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.
- data/Manifest.txt +16 -7
- data/README.rdoc +91 -0
- data/benchmark/benchmark.sql +11 -0
- data/benchmark/results.txt +28 -0
- data/benchmark/select.rb +352 -0
- data/lib/lore.rb +22 -8
- data/lib/lore/adapters/context.rb +64 -0
- data/lib/lore/adapters/postgres-pr.rb +6 -0
- data/lib/lore/adapters/postgres-pr/connection.rb +93 -0
- data/lib/lore/adapters/postgres-pr/result.rb +63 -0
- data/lib/lore/{types.rb → adapters/postgres-pr/types.rb} +36 -0
- data/lib/lore/adapters/postgres.rb +24 -0
- data/lib/lore/adapters/postgres/connection.rb +81 -0
- data/lib/lore/adapters/postgres/result.rb +82 -0
- data/lib/lore/adapters/postgres/types.rb +91 -0
- data/lib/lore/bits.rb +18 -0
- data/lib/lore/cache/abstract_entity_cache.rb +2 -1
- data/lib/lore/cache/cacheable.rb +12 -177
- data/lib/lore/cache/memcache_entity_cache.rb +89 -0
- data/lib/lore/cache/memory_entity_cache.rb +77 -0
- data/lib/lore/cache/mmap_entity_cache.rb +2 -2
- data/lib/lore/cache/mmap_entity_cache_bork.rb +86 -0
- data/lib/lore/clause.rb +107 -35
- data/lib/lore/{exception → exceptions}/ambiguous_attribute.rb +2 -2
- data/lib/lore/{exception → exceptions}/cache_exception.rb +1 -1
- data/lib/lore/exceptions/database_exception.rb +16 -0
- data/lib/lore/{exception/invalid_parameter.rb → exceptions/invalid_field.rb} +7 -4
- data/lib/lore/exceptions/unknown_type.rb +18 -0
- data/lib/lore/exceptions/validation_failure.rb +71 -0
- data/lib/lore/gui/form_generator.rb +109 -60
- data/lib/lore/gui/lore_model_select_field.rb +1 -0
- data/lib/lore/migration.rb +84 -25
- data/lib/lore/model.rb +3 -18
- data/lib/lore/{aspect.rb → model/aspect.rb} +0 -0
- data/lib/lore/model/associations.rb +225 -0
- data/lib/lore/model/attribute_settings.rb +233 -0
- data/lib/lore/model/filters.rb +34 -0
- data/lib/lore/model/mockable.rb +62 -0
- data/lib/lore/{model_factory.rb → model/model_factory.rb} +68 -39
- data/lib/lore/model/model_instance.rb +382 -0
- data/lib/lore/{model_shortcuts.rb → model/model_shortcuts.rb} +7 -0
- data/lib/lore/model/polymorphic.rb +53 -0
- data/lib/lore/model/prepare.rb +97 -0
- data/lib/lore/model/table_accessor.rb +1016 -0
- data/lib/lore/query.rb +71 -0
- data/lib/lore/query_shortcuts.rb +43 -11
- data/lib/lore/strategies/table_delete.rb +115 -0
- data/lib/lore/strategies/table_insert.rb +146 -0
- data/lib/lore/strategies/table_select.rb +299 -0
- data/lib/lore/strategies/table_update.rb +155 -0
- data/lib/lore/validation/parameter_validator.rb +85 -26
- data/lib/lore/validation/type_validator.rb +34 -78
- data/{custom_models.rb → lore-0.9.2.gem} +0 -0
- data/lore.gemspec +26 -17
- data/spec/clause.rb +37 -0
- data/spec/fixtures/blank_models.rb +37 -0
- data/{test/model.rb → spec/fixtures/models.rb} +64 -41
- data/spec/fixtures/polymorphic_models.rb +68 -0
- data/spec/model_associations.rb +86 -0
- data/spec/model_create.rb +47 -0
- data/spec/model_definition.rb +151 -0
- data/spec/model_delete.rb +31 -0
- data/spec/model_inheritance.rb +50 -0
- data/spec/model_polymorphic.rb +85 -0
- data/spec/model_select.rb +101 -0
- data/spec/model_select_eager.rb +42 -0
- data/spec/model_union_select.rb +33 -0
- data/spec/model_update.rb +45 -0
- data/spec/model_validation.rb +20 -0
- data/spec/spec_db.sql +808 -0
- data/spec/spec_env.rb +19 -0
- data/spec/spec_helpers.rb +77 -0
- metadata +93 -82
- data/lib/lore/README.txt +0 -84
- data/lib/lore/behaviours/lockable.rb +0 -55
- data/lib/lore/behaviours/movable.rb +0 -72
- data/lib/lore/behaviours/paginated.rb +0 -31
- data/lib/lore/behaviours/versioned.rb +0 -36
- data/lib/lore/connection.rb +0 -152
- data/lib/lore/exception/invalid_klass_parameters.rb +0 -63
- data/lib/lore/exception/unknown_typecode.rb +0 -19
- data/lib/lore/result.rb +0 -119
- data/lib/lore/symbol.rb +0 -58
- data/lib/lore/table_accessor.rb +0 -1790
- data/lib/lore/table_deleter.rb +0 -116
- data/lib/lore/table_inserter.rb +0 -170
- data/lib/lore/table_instance.rb +0 -389
- data/lib/lore/table_selector.rb +0 -285
- data/lib/lore/table_updater.rb +0 -157
- data/lib/lore/validation.rb +0 -65
- data/lib/lore/validation/message.rb +0 -60
- data/lib/lore/validation/reason.rb +0 -52
- data/lore_test.log +0 -2366
- data/test/README +0 -31
- data/test/custom_models.rb +0 -18
- data/test/env.rb +0 -5
- data/test/prepare.rb +0 -37
- data/test/tc_aspect.rb +0 -58
- data/test/tc_cache.rb +0 -83
- data/test/tc_clause.rb +0 -104
- data/test/tc_deep_inheritance.rb +0 -49
- data/test/tc_factory.rb +0 -57
- data/test/tc_filter.rb +0 -37
- data/test/tc_form.rb +0 -32
- data/test/tc_model.rb +0 -140
- data/test/tc_prepare.rb +0 -44
- data/test/tc_refined_query.rb +0 -88
- data/test/tc_table_accessor.rb +0 -267
- data/test/tc_thread.rb +0 -100
- data/test/test_db.sql +0 -400
- data/test/test_lore.rb +0 -50
data/lib/lore/model.rb
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
|
|
2
2
|
require('logger')
|
|
3
3
|
|
|
4
|
-
require('lore/aspect');
|
|
5
|
-
require('lore/
|
|
6
|
-
require('lore/table_inserter');
|
|
7
|
-
require('lore/table_updater');
|
|
8
|
-
require('lore/table_deleter');
|
|
9
|
-
require('lore/table_instance');
|
|
10
|
-
require('lore/query_shortcuts');
|
|
11
|
-
require('lore/validation');
|
|
12
|
-
require('lore/migration');
|
|
13
|
-
require('lore/validation/parameter_validator');
|
|
14
|
-
require('lore/exception/invalid_parameter');
|
|
15
|
-
require('lore/exception/invalid_klass_parameters');
|
|
16
|
-
require('lore/cache/cacheable');
|
|
17
|
-
require('lore/table_accessor.rb')
|
|
4
|
+
require('lore/model/aspect');
|
|
5
|
+
require('lore/model/table_accessor');
|
|
18
6
|
|
|
19
7
|
module Lore
|
|
20
8
|
|
|
@@ -77,7 +65,6 @@ module Lore
|
|
|
77
65
|
# Lore::Model extends
|
|
78
66
|
# * Lore::Cache::Cacheable
|
|
79
67
|
# * Lore::Query_Shortcuts
|
|
80
|
-
# * Lore::Validation
|
|
81
68
|
# * Lore::Aspect
|
|
82
69
|
#
|
|
83
70
|
# Each of them is optional. If you want, for example, a minimalistic
|
|
@@ -101,7 +88,6 @@ module Lore
|
|
|
101
88
|
# class User < Lore::Table_Accessor
|
|
102
89
|
# extend Lore::Query_Shortcuts
|
|
103
90
|
# extend Lore::Aspect
|
|
104
|
-
# extend Lore::Validation
|
|
105
91
|
#
|
|
106
92
|
# table :user, :public
|
|
107
93
|
# primary_key :user_id
|
|
@@ -127,9 +113,8 @@ module Lore
|
|
|
127
113
|
class Model < Table_Accessor
|
|
128
114
|
extend Lore::Cache::Cacheable
|
|
129
115
|
extend Lore::Query_Shortcuts
|
|
130
|
-
extend Lore::Validation
|
|
131
116
|
extend Lore::Aspect
|
|
132
|
-
|
|
117
|
+
# extend Lore::Migration
|
|
133
118
|
|
|
134
119
|
def by_id(pkey_id)
|
|
135
120
|
_by_id(pkey_id).first # Auto-defined in Lore::Table_Accessor.primary_key
|
|
File without changes
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
|
|
2
|
+
module Lore
|
|
3
|
+
|
|
4
|
+
class Associations
|
|
5
|
+
|
|
6
|
+
attr_reader :foreign_keys
|
|
7
|
+
attr_reader :primary_keys
|
|
8
|
+
attr_reader :base_klasses
|
|
9
|
+
attr_reader :has_a
|
|
10
|
+
attr_reader :has_n
|
|
11
|
+
attr_reader :belongs_to
|
|
12
|
+
attr_reader :aggregate_klasses
|
|
13
|
+
attr_reader :aggregates
|
|
14
|
+
attr_reader :base_klasses_tree
|
|
15
|
+
attr_reader :aggregates_tree
|
|
16
|
+
# Returns mapping rules from own foreign key values to
|
|
17
|
+
# foreign primary key values. Supports composed foreign keys.
|
|
18
|
+
# Example:
|
|
19
|
+
# {
|
|
20
|
+
# 'public.vehicle' => [ :vehicle_id ],
|
|
21
|
+
# 'public.motorizes' => [ :motorized_id ]
|
|
22
|
+
# }
|
|
23
|
+
# -->
|
|
24
|
+
# # Mapping is: [ <table>, <own key names>, <foreign pkey name> ]
|
|
25
|
+
# [
|
|
26
|
+
# 'public.vehicle', [ :vehicle_id ], [ :id ],
|
|
27
|
+
# 'public.motorized', [ :motorizes_id ], [ :id ],
|
|
28
|
+
# ]
|
|
29
|
+
# Note that this is an array, not a Hash, and entries are
|
|
30
|
+
# ordered by join order.
|
|
31
|
+
# (Which is important as arrays are ordered, opposed to Hashes
|
|
32
|
+
# in Ruby 1.8)
|
|
33
|
+
#
|
|
34
|
+
# For in-depth understanding, see
|
|
35
|
+
# Model_Instance#get_primary_key_value_map
|
|
36
|
+
attr_reader :pkey_value_lookup
|
|
37
|
+
|
|
38
|
+
# Returns polymorphic base classes as map
|
|
39
|
+
# { table => polymorphic_attribute }
|
|
40
|
+
# Example:
|
|
41
|
+
#
|
|
42
|
+
# { 'public.asset' => :concrete_asset_model }
|
|
43
|
+
#
|
|
44
|
+
attr_reader :polymorphics
|
|
45
|
+
|
|
46
|
+
attr_reader :concrete_models
|
|
47
|
+
|
|
48
|
+
def initialize(accessor)
|
|
49
|
+
@accessor = accessor
|
|
50
|
+
@foreign_keys = {}
|
|
51
|
+
@primary_keys = {}
|
|
52
|
+
|
|
53
|
+
@has_a = {}
|
|
54
|
+
@has_n = {}
|
|
55
|
+
@belongs_to = {}
|
|
56
|
+
|
|
57
|
+
@base_klasses = {}
|
|
58
|
+
@base_klasses_tree = {}
|
|
59
|
+
@aggregate_klasses = {}
|
|
60
|
+
@aggregates_tree = {}
|
|
61
|
+
|
|
62
|
+
@pkey_value_lookup = []
|
|
63
|
+
|
|
64
|
+
@polymorphics = {}
|
|
65
|
+
@concrete_models = []
|
|
66
|
+
|
|
67
|
+
@joins = false
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
private
|
|
71
|
+
|
|
72
|
+
# Add foreign keys to another model.
|
|
73
|
+
# Assumes foreign keys are stored in the models own table.
|
|
74
|
+
def add_foreign_key_to(model, *keys)
|
|
75
|
+
# {{{
|
|
76
|
+
keys.flatten!
|
|
77
|
+
mapping = [ keys, model.__associations__.primary_keys[model.table_name] ]
|
|
78
|
+
@foreign_keys[@accessor.table_name] = {} unless @foreign_keys[@accessor.table_name]
|
|
79
|
+
@foreign_keys[@accessor.table_name][model.table_name] = mapping
|
|
80
|
+
# Inherit foreign keys:
|
|
81
|
+
@foreign_keys.update(model.__associations__.foreign_keys)
|
|
82
|
+
end # }}}
|
|
83
|
+
|
|
84
|
+
public
|
|
85
|
+
|
|
86
|
+
def add_primary_key(attribute, sequence_name=nil)
|
|
87
|
+
@primary_keys[@accessor.table_name] = [] unless @primary_keys[@accessor.table_name]
|
|
88
|
+
@primary_keys[@accessor.table_name] << attribute
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Add another model as base model.
|
|
92
|
+
# Leads to inheritance of fields, primary keys,
|
|
93
|
+
# joins etc.
|
|
94
|
+
#
|
|
95
|
+
# Used by Model.is_a? Other_Model
|
|
96
|
+
#
|
|
97
|
+
def add_base_model(model, *keys)
|
|
98
|
+
# {{{
|
|
99
|
+
add_foreign_key_to(model, *keys)
|
|
100
|
+
@base_klasses[model.table_name] = [ model, *keys ]
|
|
101
|
+
@base_klasses_tree[model.table_name] = model.__associations__.base_klasses_tree
|
|
102
|
+
@aggregates_tree[model.table_name] = model.__associations__.aggregates_tree
|
|
103
|
+
keys.flatten.each { |attribute|
|
|
104
|
+
@accessor.__attributes__.set_implicit(@accessor.table_name, attribute)
|
|
105
|
+
}
|
|
106
|
+
@primary_keys.update(model.__associations__.primary_keys)
|
|
107
|
+
@pkey_value_lookup += model.__associations__.pkey_value_lookup
|
|
108
|
+
@pkey_value_lookup << [ model.table_name,
|
|
109
|
+
keys.flatten,
|
|
110
|
+
model.__associations__.primary_keys[model.table_name] ]
|
|
111
|
+
if model.is_polymorphic? then
|
|
112
|
+
@polymorphics[model.table_name] = model.polymorphic_attribute
|
|
113
|
+
model.__associations__.add_concrete_model(@accessor)
|
|
114
|
+
end
|
|
115
|
+
inherit(model)
|
|
116
|
+
end # }}}
|
|
117
|
+
|
|
118
|
+
# For polymorphic models only.
|
|
119
|
+
# Adds a concrete model class for a polymorphic
|
|
120
|
+
# model.
|
|
121
|
+
def add_concrete_model(model)
|
|
122
|
+
@concrete_models << model
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# Add another model as aggregate model.
|
|
126
|
+
# Leads to inheritance of fields, primary keys,
|
|
127
|
+
# joins etc.
|
|
128
|
+
#
|
|
129
|
+
# Used by Model.aggregates Other_Model
|
|
130
|
+
#
|
|
131
|
+
def add_aggregate_model(model, *keys)
|
|
132
|
+
# {{{
|
|
133
|
+
add_foreign_key_to(model, *keys)
|
|
134
|
+
@aggregate_klasses[model.table_name] = [ model, *keys ]
|
|
135
|
+
@aggregates_tree[model.table_name] = model.__associations__.aggregates_tree
|
|
136
|
+
# Required attributes of aggregated models are not
|
|
137
|
+
# required in this model, as aggregated models are
|
|
138
|
+
# referenced by their pkey only and have to exist
|
|
139
|
+
# in DB already.
|
|
140
|
+
# Thus, the foreign key to an aggregated model is
|
|
141
|
+
# required only:
|
|
142
|
+
keys.flatten.each { |attribute|
|
|
143
|
+
@accessor.__attributes__.set_required(attribute)
|
|
144
|
+
}
|
|
145
|
+
inherit(model)
|
|
146
|
+
end # }}}
|
|
147
|
+
|
|
148
|
+
def joined_models()
|
|
149
|
+
@joined_models || @joined_models = @aggregate_klasses.dup.update(@base_klasses)
|
|
150
|
+
end
|
|
151
|
+
alias joined_klasses joined_models
|
|
152
|
+
|
|
153
|
+
# Recursively checks if another model is aggregated
|
|
154
|
+
# by this model, either directly (foreign key is in
|
|
155
|
+
# own table) or via inheritance (foreign key is in
|
|
156
|
+
# joined table).
|
|
157
|
+
def has_aggregate_model?(model)
|
|
158
|
+
# {{{
|
|
159
|
+
@aggregate_klasses.each_pair { |table,map|
|
|
160
|
+
aggr_model = map.first
|
|
161
|
+
if aggr_model == model ||
|
|
162
|
+
aggr_model.__associations__.has_aggregate_model?(model) then
|
|
163
|
+
return true
|
|
164
|
+
end
|
|
165
|
+
}
|
|
166
|
+
return false
|
|
167
|
+
end # }}}
|
|
168
|
+
|
|
169
|
+
# Recursively checks if another model is a base model
|
|
170
|
+
# of this model, either directly (foreign key is in
|
|
171
|
+
# own table) or via inheritance (foreign key is in
|
|
172
|
+
# joined table).
|
|
173
|
+
def has_base_model?(model)
|
|
174
|
+
# {{{
|
|
175
|
+
@base_klasses.each_pair { |table,map|
|
|
176
|
+
aggr_model = map.first
|
|
177
|
+
if aggr_model == model ||
|
|
178
|
+
aggr_model.__associations__.has_base_model?(model) then
|
|
179
|
+
return true
|
|
180
|
+
end
|
|
181
|
+
}
|
|
182
|
+
return false
|
|
183
|
+
end # }}}
|
|
184
|
+
|
|
185
|
+
# Recursively checks if another model is joined
|
|
186
|
+
# by this model (aggregated or as base model), either
|
|
187
|
+
# directly (foreign key is in own table) or via
|
|
188
|
+
# inheritance (foreign key is in joined table).
|
|
189
|
+
def has_joined_model?(model)
|
|
190
|
+
has_base_model?(model) || has_aggregated_model?(model)
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def joins()
|
|
195
|
+
@joins || @joins = @aggregates_tree.dup.update(@base_klasses_tree)
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# For cat.get_wheel()
|
|
199
|
+
def add_has_a(model, *keys)
|
|
200
|
+
add_foreign_key_to(model, *keys)
|
|
201
|
+
@has_a[@accessor.table_name] = model
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
# For cat.get_wheel_set()
|
|
205
|
+
def add_has_n(model, *keys)
|
|
206
|
+
add_foreign_key_to(model, *keys)
|
|
207
|
+
@has_n[@accessor.table_name] = model
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
# For wheel.get_car()
|
|
211
|
+
def add_belongs_to(model, *keys)
|
|
212
|
+
add_foreign_key_to(model, *keys)
|
|
213
|
+
@belongs_to[@accessor.table_name] = model
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
def inherit(base_model)
|
|
217
|
+
parent_associations = base_model.__associations__
|
|
218
|
+
@has_a.update(parent_associations.has_a)
|
|
219
|
+
@has_n.update(parent_associations.has_n)
|
|
220
|
+
@belongs_to.update(parent_associations.belongs_to)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
end
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
|
|
2
|
+
module Lore
|
|
3
|
+
|
|
4
|
+
# There are several categories of attributes:
|
|
5
|
+
#
|
|
6
|
+
# [required] - A value for this attribute has to be set in any case
|
|
7
|
+
# (field is NOT NULL)
|
|
8
|
+
# [implicit] - Value for this attribute will be set by database.
|
|
9
|
+
# (field is set via sequence, trigger, etc.)
|
|
10
|
+
# Any given value will be ignored.
|
|
11
|
+
#
|
|
12
|
+
# If none of the above, field will be treaded as NULL field.
|
|
13
|
+
#
|
|
14
|
+
class Attribute_Settings
|
|
15
|
+
|
|
16
|
+
attr_accessor :fields, :fields_flat, :required, :implicit, :types, :constraints, :sequences, :primary_keys
|
|
17
|
+
|
|
18
|
+
def initialize(accessor, fields, types)
|
|
19
|
+
fields.map! { |a| a.to_sym }
|
|
20
|
+
@accessor = accessor
|
|
21
|
+
fields.map! { |f| f.to_sym }
|
|
22
|
+
@fields = { accessor.table_name => fields }
|
|
23
|
+
# @fields_flat = fields
|
|
24
|
+
@required = {}
|
|
25
|
+
@implicit = {}
|
|
26
|
+
@types = {}
|
|
27
|
+
@constraints = {}
|
|
28
|
+
@hidden = {}
|
|
29
|
+
@sequences = {}
|
|
30
|
+
@primary_keys = {}
|
|
31
|
+
sym_types = {}
|
|
32
|
+
types.each_pair { |attrib, type|
|
|
33
|
+
sym_types[attrib.to_sym] = type
|
|
34
|
+
}
|
|
35
|
+
@types[accessor.table_name] = sym_types
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def [](table_name)
|
|
39
|
+
@fields[table_name]
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def num_fields
|
|
43
|
+
fields_flat.length
|
|
44
|
+
end
|
|
45
|
+
def num_own_fields
|
|
46
|
+
fields[@accessor.table_name].length
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def required?(attribute)
|
|
50
|
+
# Inherited primary keys are marked as required,
|
|
51
|
+
# but they aren't in this model, where they are
|
|
52
|
+
# also marked as implicit.
|
|
53
|
+
@required[attribute] && !@implicit[attribute] || false
|
|
54
|
+
end
|
|
55
|
+
def implicit?(attribute)
|
|
56
|
+
@implicit[attribute]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def set_required(*args)
|
|
60
|
+
table = @accessor.table_name
|
|
61
|
+
if args.length == 1 then
|
|
62
|
+
attribute = args.at(0)
|
|
63
|
+
else
|
|
64
|
+
table = args.at(0)
|
|
65
|
+
attribute = args.at(1)
|
|
66
|
+
end
|
|
67
|
+
@required[table] = {} unless @required[table]
|
|
68
|
+
@required[table][attribute.to_sym] = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# Implicit attributes are set by the DBMS, via sequences,
|
|
72
|
+
# triggers, or may not be set manually for some reason.
|
|
73
|
+
# Manually set values for implicit attributes are ignored
|
|
74
|
+
# on INSERT and UPDATE commands, but may be used in e.g.
|
|
75
|
+
# WHERE part of a query.
|
|
76
|
+
#
|
|
77
|
+
# Usage:
|
|
78
|
+
#
|
|
79
|
+
# set_implicit(table, :attrib_a)
|
|
80
|
+
# Or
|
|
81
|
+
# set_implicit(:attrib_a) # table defaults to own table
|
|
82
|
+
#
|
|
83
|
+
def set_implicit(*args)
|
|
84
|
+
table = nil
|
|
85
|
+
attribute = nil
|
|
86
|
+
if args.length > 1 then
|
|
87
|
+
table = args.at(0)
|
|
88
|
+
attribute = args.at(1).to_sym
|
|
89
|
+
else
|
|
90
|
+
table = @accessor.table_name
|
|
91
|
+
attribute = args.at(0).to_sym
|
|
92
|
+
end
|
|
93
|
+
@implicit[table] = [] unless @implicit[table]
|
|
94
|
+
@implicit[table] << attribute
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def add_primary_key(attribute, sequence_name=nil)
|
|
98
|
+
@primary_keys[@accessor.table_name] = [] unless @primary_keys[@accessor.table_name]
|
|
99
|
+
@primary_keys[@accessor.table_name] << attribute
|
|
100
|
+
if sequence_name then
|
|
101
|
+
set_sequence(attribute, sequence_name) if sequence_name
|
|
102
|
+
else
|
|
103
|
+
set_required(attribute)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def set_sequence(attribute, sequence_name)
|
|
108
|
+
set_implicit(attribute)
|
|
109
|
+
if @sequences[@accessor.table_name] then
|
|
110
|
+
@sequences[@accessor.table_name][attribute] = sequence_name
|
|
111
|
+
else
|
|
112
|
+
@sequences[@accessor.table_name] = { attribute => sequence_name }
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# All attributes that aren't implicit and thus
|
|
117
|
+
# can be set manually.
|
|
118
|
+
def explicit
|
|
119
|
+
return @explicit if @explicit
|
|
120
|
+
@explicit = {}
|
|
121
|
+
@fields.each_pair { |table, attrib_list|
|
|
122
|
+
@explicit[table] = attrib_list.reject { |a|
|
|
123
|
+
# @fields includes all inherited fields.
|
|
124
|
+
# We do not want to aggregated models to
|
|
125
|
+
# extend @expected and @implicit, as they
|
|
126
|
+
# can only be referenced by their primary
|
|
127
|
+
# keys, but never assigned values in e.g.
|
|
128
|
+
# Model.create.
|
|
129
|
+
!@implicit[table] || @implicit[table].include?(a.to_sym)
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
@explicit.delete_if { |table, fields| fields.length == 0 }
|
|
133
|
+
@explicit
|
|
134
|
+
end
|
|
135
|
+
|
|
136
|
+
def add_hidden(attribute)
|
|
137
|
+
@hidden[@accessor.table_name] = attribute
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
def add_base_model(model)
|
|
141
|
+
inherit(model)
|
|
142
|
+
@sequences.update(model.__attributes__.sequences)
|
|
143
|
+
@implicit.update(model.__attributes__.implicit)
|
|
144
|
+
@required.update(model.__attributes__.required)
|
|
145
|
+
@fields_flat = false # Invalidate cached values
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def add_aggregate_model(model)
|
|
149
|
+
inherit(model)
|
|
150
|
+
# Include indirect implicit fields, but do not
|
|
151
|
+
# set aggregated primary keys as implicit, as it
|
|
152
|
+
# will be set manually:
|
|
153
|
+
aggregated_implicit = model.__attributes__.implicit.dup
|
|
154
|
+
# model.get_primary_keys.each { |pkey_field|
|
|
155
|
+
# aggregated_implicit[model.table_name].delete(pkey_field)
|
|
156
|
+
# }
|
|
157
|
+
# @implicit.update(aggregated_implicit)
|
|
158
|
+
@fields_flat = false # Invalidate cached values
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def fields_flat
|
|
162
|
+
return @fields_flat if @fields_flat
|
|
163
|
+
fields = @fields[@accessor.table_name]
|
|
164
|
+
fields += fields_flat_rec
|
|
165
|
+
# Shadowing attribute fields whose name is already
|
|
166
|
+
# present in more specific table. Remove uniq! to
|
|
167
|
+
# allow multiple appearance of attribute names in
|
|
168
|
+
# flat field list.
|
|
169
|
+
# TODO: CHECK THIS!!
|
|
170
|
+
# fields.uniq!
|
|
171
|
+
@fields_flat = fields
|
|
172
|
+
return @fields_flat
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def fields_flat_rec(model=nil)
|
|
176
|
+
fields = []
|
|
177
|
+
model ||= @accessor
|
|
178
|
+
joined_models = model.__associations__.joined_models
|
|
179
|
+
model.__associations__.joins.each_pair { |table,base_tables|
|
|
180
|
+
fields += @fields[table]
|
|
181
|
+
fields += fields_flat_rec(joined_models[table].first)
|
|
182
|
+
}
|
|
183
|
+
fields
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
def inherit(base_model)
|
|
187
|
+
parent_attributes = base_model.__attributes__
|
|
188
|
+
@constraints.update(parent_attributes.constraints)
|
|
189
|
+
@types.update(parent_attributes.types)
|
|
190
|
+
@fields.update(parent_attributes.fields)
|
|
191
|
+
# @fields_flat += parent_attributes.fields_flat
|
|
192
|
+
@primary_keys.update(parent_attributes.primary_keys)
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
def add_constraints(attrib, constraints={})
|
|
196
|
+
if attrib.kind_of? Clause then
|
|
197
|
+
attrib_split = attrib.to_s.split('.')
|
|
198
|
+
table = attrib_split[0..-2]
|
|
199
|
+
attrib = attrib_split[-1]
|
|
200
|
+
else
|
|
201
|
+
table = @accessor.table_name
|
|
202
|
+
end
|
|
203
|
+
attrib = attrib.to_sym unless attrib.is_a? Symbol
|
|
204
|
+
|
|
205
|
+
@constraints[table] = Hash.new unless @constraints[table]
|
|
206
|
+
@constraints[table][attrib] = Hash.new unless @constraints[table][attrib]
|
|
207
|
+
|
|
208
|
+
if constraints[:mandatory] then
|
|
209
|
+
set_required(table, attrib.to_s)
|
|
210
|
+
end
|
|
211
|
+
if constraints[:format] then
|
|
212
|
+
@constraints[table][attrib][:format] = constraints[:format]
|
|
213
|
+
end
|
|
214
|
+
if constraints[:length] then
|
|
215
|
+
if constraints[:length].kind_of? Range then
|
|
216
|
+
@constraints[table][attrib][:minlength] = constraints[:length].first
|
|
217
|
+
@constraints[table][attrib][:maxlength] = constraints[:length].last
|
|
218
|
+
else
|
|
219
|
+
@constraints[table][attrib][:minlength] = constraints[:length]
|
|
220
|
+
@constraints[table][attrib][:maxlength] = constraints[:length]
|
|
221
|
+
end
|
|
222
|
+
end
|
|
223
|
+
if constraints[:minlength] then
|
|
224
|
+
@constraints[table][attrib][:minlength] = constraints[:minlength]
|
|
225
|
+
end
|
|
226
|
+
if constraints[:maxlength] then
|
|
227
|
+
@constraints[table][attrib][:maxlength] = constraints[:maxlength]
|
|
228
|
+
end
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
end
|