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,20 +1,8 @@
1
1
 
2
2
  require('logger')
3
3
 
4
- require('lore/aspect');
5
- require('lore/table_selector');
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
- extend Lore::Migration
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