og 0.9.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: time.rb 202 2005-01-17 10:44:13Z gmosx $
3
+ # $Id: time.rb 259 2005-02-15 08:54:54Z gmosx $
4
4
 
5
- require "time.rb"
5
+ require 'time.rb'
6
6
 
7
7
  module N;
8
8
 
@@ -8,7 +8,7 @@ module N
8
8
  # objects. Typically used in Validator objects but can be
9
9
  # included in managed objects too.
10
10
  #
11
- # == Example
11
+ # === Example
12
12
  #
13
13
  # class User
14
14
  # prop_accessor :name, String
@@ -50,8 +50,6 @@ module N
50
50
 
51
51
  module Validation
52
52
 
53
- # = Errors
54
- #
55
53
  # Encapsulates a list of validation errors.
56
54
 
57
55
  class Errors
@@ -159,8 +157,6 @@ module Validation
159
157
  base.extend(MetaLanguage)
160
158
  end
161
159
 
162
- # = MetaLanguage
163
- #
164
160
  # Implements the Validation meta-language.
165
161
 
166
162
  module MetaLanguage
@@ -172,9 +168,9 @@ module Validation
172
168
  # Validates that the attributes have a values, ie they are
173
169
  # neither nil or empty.
174
170
  #
175
- # == Example
171
+ # === Example
176
172
  #
177
- # validate_value :, :msg => 'No confirmation'
173
+ # validate_value :param, :msg => 'No confirmation'
178
174
 
179
175
  def validate_value(*params)
180
176
  c = {
@@ -199,7 +195,7 @@ module Validation
199
195
 
200
196
  # Validates the confirmation of +String+ attributes.
201
197
  #
202
- # == Example
198
+ # === Example
203
199
  #
204
200
  # validate_confirmation :password, :msg => 'No confirmation'
205
201
 
@@ -228,7 +224,7 @@ module Validation
228
224
 
229
225
  # Validates the format of +String+ attributes.
230
226
  #
231
- # == Example
227
+ # === Example
232
228
  #
233
229
  # validate_format :name, :format => /$A*/, :msg => 'My error', :on => :create
234
230
 
@@ -263,7 +259,7 @@ module Validation
263
259
 
264
260
  # Validates the length of +String+ attributes.
265
261
  #
266
- # == Example
262
+ # === Example
267
263
  #
268
264
  # validate_length :name, :max => 30, :msg => 'Too long'
269
265
  # validate_length :name, :min => 2, :msg => 'Too sort'
@@ -354,7 +350,7 @@ module Validation
354
350
  # Validates that the attributes are included in
355
351
  # an enumeration.
356
352
  #
357
- # == Example
353
+ # === Example
358
354
  #
359
355
  # validate_inclusion :sex, :in => %w{ Male Female }, :msg => 'huh??'
360
356
  # validate_inclusion :age, :in => 5..99
data/lib/og.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  # * George Moschovitis <gm@navel.gr>
2
2
  # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: og.rb 248 2005-01-31 13:38:34Z gmosx $
3
+ # $Id: og.rb 259 2005-02-15 08:54:54Z gmosx $
4
4
 
5
+ require 'glue'
5
6
  require 'glue/logger'
6
7
  require 'glue/attribute'
7
8
  require 'glue/property'
@@ -9,6 +10,7 @@ require 'glue/array'
9
10
  require 'glue/hash'
10
11
  require 'glue/time'
11
12
  require 'glue/pool'
13
+ require 'glue/validation'
12
14
 
13
15
  # Og (ObjectGraph) is an efficient, yet simple Object-Relational
14
16
  # mapping library.
@@ -19,10 +21,10 @@ require 'glue/pool'
19
21
  #
20
22
  # + Object-Relational mapping.
21
23
  # + Absolutely no configuration files.
22
- # + Multiple backends (PostgreSQL, MySQL).
24
+ # + Multiple backends (PostgreSQL, MySQL, SQLite).
23
25
  # + ActiveRecord-style meta language and db aware methods.
24
- # + Deserialize to Ruby Objects or ResultSets.
25
- # + Deserialize sql join queries to Ruby Objects.
26
+ # + Deserialize to Ruby Objects.
27
+ # + Deserialize sql join queries to Ruby Objects (temporarily dissabled).
26
28
  # + Serialize arbitrary ruby object graphs through YAML.
27
29
  # + Connection pooling.
28
30
  # + Thread safety.
@@ -89,6 +91,18 @@ require 'glue/pool'
89
91
 
90
92
  class Og
91
93
 
94
+ # The name.
95
+
96
+ Name = 'ObjectGraph'
97
+
98
+ # The version.
99
+
100
+ Version = '0.10.0'
101
+
102
+ # Library path.
103
+
104
+ LibPath = File.dirname(__FILE__)
105
+
92
106
  # If true, only allow reading from the database. Usefull
93
107
  # for maintainance.
94
108
 
@@ -131,267 +145,19 @@ class Og
131
145
  @@db.get_connection
132
146
  end
133
147
 
134
- end
135
-
136
- # gmosx: leave this here.
137
- require 'og/enchant'
138
- require 'og/meta'
139
-
140
- class Og
141
-
142
- # Marker module. If included this in a class, the Og automanager
143
- # ignores this class.
148
+ # The adapter of the active database.
144
149
 
145
- module Unmanageable; end
146
-
147
- # Encapsulates an Og Database.
148
-
149
- class Database
150
- include Og::Enchant
151
-
152
- # Managed class metadata
153
-
154
- class ManagedClassMeta
155
- # The managed class.
156
- attr_accessor :klass
157
-
158
- # A mapping of the database fields to the object properties.
159
- attr_accessor :field_index
160
-
161
- def initialize(klass = nil)
162
- @klass = klass
163
- @field_index = {}
164
- end
150
+ def self.adapter
151
+ @@db.adapter
165
152
  end
166
153
 
167
- # hash of configuration options.
154
+ # Marker module. If included this in a class, the Og automanager
155
+ # ignores this class.
168
156
 
169
- attr_accessor :config
157
+ module Unmanageable; end
170
158
 
171
- # Pool of connections to the backend.
172
-
173
- attr_accessor :connection_pool
174
-
175
- # Managed classes.
176
-
177
- attr_accessor :managed_classes
178
-
179
- # Initialize the database interface.
180
-
181
- def initialize(config)
182
- @config = config
183
-
184
- # populate with default options if needed.
185
- @config[:connection_count] ||= 1
186
-
187
- # require the backend.
188
- backend = @config[:backend] || "psql"
189
- require "og/backends/#{backend}"
190
- eval %{ @config[:backend] = #{backend.capitalize}Backend }
191
-
192
- @connection_pool = N::Pool.new
193
- @managed_classes = N::SafeHash.new
194
-
195
- Logger.info "Connecting to database '#{@config[:database]}' using backend '#{backend}'."
196
-
197
- @config[:connection_count].times do
198
- @connection_pool << Og::Connection.new(self)
199
- end
200
-
201
- # gmosx, FIXME: this automanage code is not elegant and slow
202
- # should probably recode this, along with glue/property.rb
203
-
204
- if Og.auto_manage_classes
205
- # automatically manage classes with properties and metadata.
206
- # gmosx: Any idea how to optimize this?
207
- classes_to_manage = []
208
- ObjectSpace.each_object(Class) do |c|
209
- if c.respond_to?(:__props) and c.__props
210
- classes_to_manage << c
211
- end
212
- end
213
- Logger.info "Og auto manages the following classes:"
214
- Logger.info "#{classes_to_manage.inspect}"
215
- manage_classes(*classes_to_manage)
216
- end
217
-
218
- # use the newly created database.
219
- Og.use(self)
220
- end
221
-
222
- # Shutdown the database interface.
223
-
224
- def shutdown
225
- for con in @connection_pool
226
- con.close()
227
- end
228
- end
229
- alias_method :close, :shutdown
230
-
231
- # Get a connection from the pool to access the database.
232
- # Stores the connection in a thread-local variable.
233
-
234
- def get_connection
235
- thread = Thread.current
236
-
237
- unless conn = thread[:og_conn]
238
- conn = @connection_pool.pop()
239
- thread[:og_conn] = conn
240
- end
241
-
242
- return conn
243
- end
244
- alias_method :connection, :get_connection
245
-
246
- # Restore an unused connection to the pool.
247
-
248
- def put_connection
249
- thread = Thread.current
250
-
251
- if conn = thread[:og_conn]
252
- thread[:og_conn] = nil
253
- return @connection_pool.push(conn)
254
- end
255
- end
256
-
257
- # Utility method, automatically restores a connection to the pool.
258
-
259
- def connect(deserialize = nil, &block)
260
- result = nil
261
-
262
- begin
263
- conn = get_connection()
264
- conn.deserialize = deserialize unless deserialize.nil?
265
-
266
- result = yield(conn)
267
-
268
- conn.deserialize = true
269
- ensure
270
- put_connection()
271
- end
272
-
273
- return result
274
- end
275
- alias_method :open, :connect
276
-
277
-
278
- # Register a standard Ruby class as managed.
279
-
280
- def manage(klass)
281
- return if managed?(klass) or klass.ancestors.include?(Og::Unmanageable)
282
-
283
- @managed_classes[klass] = ManagedClassMeta.new(klass)
284
-
285
- # Add standard og methods to the class.
286
- convert(klass)
287
-
288
- # Add helper methods to the class.
289
- enchant(klass) if Og.enchant_managed_classes
290
- end
291
-
292
- # Helper method to set multiple managed classes.
293
-
294
- def manage_classes(*klasses)
295
- for klass in klasses
296
- manage(klass)
297
- end
298
- end
299
-
300
- # Stop managing a Ruby class
301
-
302
- def unmanage(klass)
303
- @managed_classes.delete(klass)
304
- end
305
-
306
- # Is this class managed?
307
- #
308
- def managed?(klass)
309
- return @managed_classes.include?(klass)
310
- end
311
-
312
- # Add standard og functionality to the class
313
-
314
- def convert(klass)
315
- # Grab backend class
316
- backend = @config[:backend]
317
-
318
- # gmosx: this check is needed to allow the developer to customize
319
- # the sql generated for oid
320
- backend.eval_og_oid(klass) unless klass.instance_methods.include?(:oid)
321
-
322
- klass.class_eval %{
323
- DBTABLE = "#{backend.table(klass)}"
324
- DBSEQ = "#{backend.table(klass)}_oids_seq"
325
-
326
- def to_i()
327
- @oid
328
- end
329
- }
330
-
331
- # Create the schema for this class if not available.
332
- create_table(klass)
333
-
334
- # Precompile some code that gets executed all the time.
335
- # Deletion code is not precompiled, because it is not used
336
- # as frequently.
337
- backend.eval_og_insert(klass)
338
- backend.eval_og_update(klass)
339
- backend.eval_og_deserialize(klass, self)
340
- end
341
-
342
- # Automatically wrap connection methods.
343
- #
344
- def self.wrap_method(method, args)
345
- args = args.split(/,/)
346
- class_eval %{
347
- def #{method}(#{args.join(", ")})
348
- thread = Thread.current
349
-
350
- unless conn = thread[:og_conn]
351
- conn = @connection_pool.pop()
352
- thread[:og_conn] = conn
353
- end
354
-
355
- return conn.#{method}(#{args.collect {|a| a.split(/=/)[0]}.join(", ")})
356
- end
357
- }
358
- end
359
-
360
- wrap_method :create_table, "klass"
361
- wrap_method :drop_table, "klass"
362
- wrap_method :save, "obj"; alias_method :<<, :save; alias_method :put, :save
363
- wrap_method :insert, "obj"
364
- wrap_method :update, "obj"
365
- wrap_method :update_properties, "update_sql, obj_or_oid, klass = nil"
366
- wrap_method :pupdate, "update_sql, obj_or_oid, klass = nil"
367
- wrap_method :load, "oid, klass"; alias_method :get, :load
368
- wrap_method :load_by_oid, "oid, klass"
369
- wrap_method :load_by_name, "name, klass"
370
- wrap_method :load_all, "klass, extrasql = nil"
371
- wrap_method :select, "sql, klass"
372
- wrap_method :select_one, "sql, klass"
373
- wrap_method :count, "sql, klass = nil"
374
- wrap_method :delete, "obj_or_oid, klass = nil"
375
- wrap_method :query, "sql"
376
- wrap_method :exec, "sql"
377
-
378
- class << self
379
- def create_db!(config)
380
- get_connection().db.create_db(config[:database], config[:user],
381
- config[:password])
382
- end
383
- alias_method :create!, :create_db!
384
-
385
- def drop_db!(config)
386
- backend = config[:backend] || "psql"
387
- require "og/backends/#{backend}"
388
- eval %{
389
- #{backend.capitalize}Backend.drop_db(config[:database], config[:user],
390
- config[:password])
391
- }
392
- end
393
- alias_method :drop!, :drop_db!
394
- end
395
159
  end
396
160
 
397
- end
161
+ # gmosx: leave this here.
162
+
163
+ require 'og/database'
@@ -0,0 +1,352 @@
1
+ # * George Moschovitis <gm@navel.gr>
2
+ # (c) 2004-2005 Navel, all rights reserved.
3
+ # $Id: adapter.rb 255 2005-02-10 12:45:32Z gmosx $
4
+
5
+ require 'yaml'
6
+ require 'singleton'
7
+
8
+ require 'og/connection'
9
+
10
+ class Og
11
+
12
+ # An adapter communicates with the backend datastore.
13
+ # The adapters for all supported datastores extend this
14
+ # class. Typically, an RDBMS is used to implement a
15
+ # datastore.
16
+
17
+ class Adapter
18
+ include Singleton
19
+
20
+ # A mapping between Ruby and backend Datastore types.
21
+
22
+ attr_accessor :typemap
23
+
24
+ # Lookup the adapter instance from the adapter name.
25
+
26
+ def self.for_name(name)
27
+ require "og/adapters/#{name}"
28
+ eval %{ return #{name.capitalize}Adapter.instance }
29
+ end
30
+
31
+ def initialize
32
+ @typemap = {
33
+ Integer => 'integer',
34
+ Fixnum => 'integer',
35
+ Float => 'float',
36
+ String => 'text',
37
+ Time => 'timestamp',
38
+ Date => 'date',
39
+ TrueClass => 'boolean',
40
+ Object => 'text',
41
+ Array => 'text',
42
+ Hash => 'text'
43
+ }
44
+ end
45
+
46
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
47
+ # :section: Utilities
48
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
49
+
50
+ # Escape an SQL string
51
+
52
+ def self.escape(str)
53
+ return nil unless str
54
+ return str.gsub( /'/, "''" )
55
+ end
56
+
57
+ # Convert a ruby time to an sql timestamp.
58
+ # TODO: Optimize this
59
+
60
+ def self.timestamp(time = Time.now)
61
+ return nil unless time
62
+ return time.strftime("%Y-%m-%d %H:%M:%S")
63
+ end
64
+
65
+ # Output YYY-mm-dd
66
+ # TODO: Optimize this
67
+
68
+ def self.date(date)
69
+ return nil unless date
70
+ return "#{date.year}-#{date.month}-#{date.mday}"
71
+ end
72
+
73
+ # Parse sql datetime
74
+ # TODO: Optimize this
75
+
76
+ def self.parse_timestamp(str)
77
+ return Time.parse(str)
78
+ end
79
+
80
+ # Input YYYY-mm-dd
81
+ # TODO: Optimize this
82
+
83
+ def self.parse_date(str)
84
+ return nil unless str
85
+ return Date.strptime(str)
86
+ end
87
+
88
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
89
+ # :section: Database methods
90
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
91
+
92
+ # Create the database.
93
+
94
+ def create_db(database, user = nil, password = nil)
95
+ Logger.info "Creating database '#{database}'."
96
+ end
97
+
98
+ # Drop the database.
99
+
100
+ def drop_db(database, user = nil, password = nil)
101
+ Logger.info "Dropping database '#{database}'."
102
+ end
103
+
104
+ # Create a new connection to the backend.
105
+
106
+ def new_connection(db)
107
+ return Og::Connection.new(db)
108
+ end
109
+
110
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
111
+ # :section: OR mapping methods and utilities.
112
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
113
+
114
+ # Encode the name of the klass as an sql safe string.
115
+ # The Module separators are replaced with _ and NOT stripped
116
+ # out so that we can convert back to the original notation if
117
+ # needed. The leading module if available is removed.
118
+
119
+ def self.encode(klass)
120
+ "#{klass.name.gsub(/^.*::/, "")}".gsub(/::/, "_").downcase
121
+ end
122
+
123
+ # The name of the SQL table where objects of this class
124
+ # are stored.
125
+
126
+ def self.table(klass)
127
+ "_#{Og.table_prefix}#{encode(klass)}"
128
+ end
129
+
130
+ # The name of the join table for the two given classes.
131
+
132
+ def self.join_table(klass1, klass2)
133
+ "_#{Og.table_prefix}j_#{encode(klass1)}_#{encode(klass2)}"
134
+ end
135
+
136
+ # Return an sql string evaluator for the property.
137
+ # No need to optimize this, used only to precalculate code.
138
+ # YAML is used to store general Ruby objects to be more
139
+ # portable.
140
+ #
141
+ # FIXME: add extra handling for float.
142
+
143
+ def write_prop(p)
144
+ if p.klass.ancestors.include?(Integer)
145
+ return "#\{@#{p.symbol} || 'NULL'\}"
146
+ elsif p.klass.ancestors.include?(Float)
147
+ return "#\{@#{p.symbol} || 'NULL'\}"
148
+ elsif p.klass.ancestors.include?(String)
149
+ return "'#\{#{self.class}.escape(@#{p.symbol})\}'"
150
+ elsif p.klass.ancestors.include?(Time)
151
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
152
+ elsif p.klass.ancestors.include?(Date)
153
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
154
+ elsif p.klass.ancestors.include?(TrueClass)
155
+ return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
156
+ else
157
+ return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
158
+ end
159
+ end
160
+
161
+ # Return an evaluator for reading the property.
162
+ # No need to optimize this, used only to precalculate code.
163
+
164
+ def read_prop(p, idx)
165
+ if p.klass.ancestors.include?(Integer)
166
+ return "res[#{idx}].to_i"
167
+ elsif p.klass.ancestors.include?(Float)
168
+ return "res[#{idx}].to_f"
169
+ elsif p.klass.ancestors.include?(String)
170
+ return "res[#{idx}]"
171
+ elsif p.klass.ancestors.include?(Time)
172
+ return "#{self.class}.parse_timestamp(res[#{idx}])"
173
+ elsif p.klass.ancestors.include?(Date)
174
+ return "#{self.class}.parse_date(res[#{idx}])"
175
+ elsif p.klass.ancestors.include?(TrueClass)
176
+ return "('0' != res[#{idx}])"
177
+ else
178
+ return "YAML::load(res[#{idx}])"
179
+ end
180
+ end
181
+
182
+ # Create the fields that correpsond to the klass properties.
183
+ # The generated fields array is used in create_table.
184
+ # If the property has an :sql metadata this overrides the
185
+ # default mapping. If the property has an :extra_sql metadata
186
+ # the extra sql is appended after the default mapping.
187
+
188
+ def create_fields(klass)
189
+ fields = []
190
+
191
+ klass.__props.each do |p|
192
+ klass.sql_index(p.symbol) if p.meta[:sql_index]
193
+
194
+ field = "#{p.symbol}"
195
+
196
+ if p.meta and p.meta[:sql]
197
+ field << " #{p.meta[:sql]}"
198
+ else
199
+ field << " #{@typemap[p.klass]}"
200
+ # attach extra sql
201
+ if p.meta and extra_sql = p.meta[:extra_sql]
202
+ field << " #{extra_sql}"
203
+ end
204
+ end
205
+
206
+ fields << field
207
+ end
208
+
209
+ return fields
210
+ end
211
+
212
+ # Create the managed object table. The properties of the
213
+ # object are mapped to the table columns. Additional sql relations
214
+ # and constrains are created (indicices, sequences, etc).
215
+
216
+ def create_table(klass)
217
+ raise 'Not implemented!'
218
+ end
219
+
220
+ # Returns the props that will be included in the insert query.
221
+ # For some backends the oid should be stripped.
222
+
223
+ def props_for_insert(klass)
224
+ klass.__props
225
+ end
226
+
227
+ # Returns the code that actually inserts the object into the
228
+ # database. Returns the code as String.
229
+
230
+ def insert_code(klass, sql, pre_cb, post_cb)
231
+ raise 'Not implemented!'
232
+ end
233
+
234
+ # Generate the mapping of the database fields to the
235
+ # object properties.
236
+
237
+ def calc_field_index(klass, og)
238
+ raise 'Not implemented!'
239
+ end
240
+
241
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
242
+ # :section: Managed object enchant methods
243
+ # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
244
+
245
+ # Generate the property for oid.
246
+
247
+ def eval_og_oid(klass)
248
+ klass.class_eval %{
249
+ prop_accessor :oid, Fixnum, :sql => "integer PRIMARY KEY"
250
+ }
251
+ end
252
+
253
+ # Precompile the insert code for the given class.
254
+ # The generated code sets the oid when inserting!
255
+
256
+ def eval_og_insert(klass, db)
257
+ if klass.instance_methods.include?('og_pre_insert')
258
+ pre_cb = 'og_pre_insert(conn);'
259
+ else
260
+ pre_cb = ''
261
+ end
262
+
263
+ if klass.instance_methods.include?('og_post_insert')
264
+ post_cb = 'og_post_insert(conn);'
265
+ else
266
+ post_cb = ''
267
+ end
268
+
269
+ if klass.instance_methods.include?('og_pre_insert_update')
270
+ pre_cb << 'og_pre_insert_update(conn);'
271
+ end
272
+
273
+ if klass.instance_methods.include?('og_post_insert_update')
274
+ post_cb << 'og_post_insert_update(conn);'
275
+ end
276
+
277
+ klass.class_eval %{
278
+ def og_insert(conn)
279
+ #{insert_code(klass, db, pre_cb, post_cb)}
280
+ end
281
+ }
282
+ end
283
+
284
+ # Precompile the update code for the given class.
285
+ # Ignore the oid when updating!
286
+
287
+ def eval_og_update(klass, db)
288
+ props = klass.__props.reject { |p| :oid == p.symbol }
289
+
290
+ updates = props.collect { |p|
291
+ "#{p.name}=#{write_prop(p)}"
292
+ }
293
+
294
+ sql = "UPDATE #{klass::DBTABLE} SET #{updates.join(', ')} WHERE oid=#\{@oid\}"
295
+
296
+ if klass.instance_methods.include?('og_pre_update')
297
+ pre_cb = 'og_pre_update(conn);'
298
+ else
299
+ pre_cb = ''
300
+ end
301
+
302
+ if klass.instance_methods.include?('og_post_update')
303
+ post_cb = 'og_post_update(conn);'
304
+ else
305
+ post_cb = ''
306
+ end
307
+
308
+ if klass.instance_methods.include?('og_pre_insert_update')
309
+ pre_cb << 'og_pre_insert_update(conn);'
310
+ end
311
+
312
+ if klass.instance_methods.include?('og_post_insert_update')
313
+ post_cb << 'og_post_insert_update(conn);'
314
+ end
315
+
316
+ klass.class_eval %{
317
+ def og_update(conn)
318
+ #{pre_cb}
319
+ conn.exec "#{sql}"
320
+ #{post_cb}
321
+ end
322
+ }
323
+ end
324
+
325
+ # Precompile the code to read (deserialize) objects of the
326
+ # given class from the backend. In order to allow for changing
327
+ # field/attribute orders we have to use a field mapping hash.
328
+
329
+ def eval_og_read(klass, db)
330
+ calc_field_index(klass, db)
331
+
332
+ props = klass.__props
333
+ code = []
334
+
335
+ props.each do |p|
336
+ if idx = db.managed_classes[klass].field_index[p.name]
337
+ # more fault tolerant if a new field is added and it
338
+ # doesnt exist in the database.
339
+ code << "@#{p.name} = #{read_prop(p, idx)}"
340
+ end
341
+ end
342
+
343
+ klass.class_eval %{
344
+ def og_read(res, tuple = nil)
345
+ #{code.join('; ')}
346
+ end
347
+ }
348
+ end
349
+
350
+ end
351
+
352
+ end