rod 0.6.0 → 0.6.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,66 @@
1
+ module Rod
2
+ # A base class for all classes stored in the DataBase
3
+ # (both user defined and DB defined).
4
+ class AbstractModel
5
+ # Empty string.
6
+ def self.typedef_struct
7
+ raise RodException.new("#typdef_struct called for AbstractModel")
8
+ end
9
+
10
+ # C-struct name of the model.
11
+ def self.struct_name
12
+ raise RodException.new("#typdef_struct called for AbstractModel")
13
+ end
14
+
15
+ # Path to the file storing the model data.
16
+ def self.path_for_data(path)
17
+ "#{path}#{self.struct_name}.dat"
18
+ end
19
+
20
+ # Default implementation prints nothing.
21
+ def self.layout
22
+ end
23
+
24
+ # By default there are no fields.
25
+ def self.fields
26
+ []
27
+ end
28
+
29
+ # By default nothing is built.
30
+ def self.build_structure
31
+ end
32
+
33
+ # Default cache for models.
34
+ def self.cache
35
+ @cache ||= Cache.new
36
+ end
37
+
38
+ # There are no indexed properties.
39
+ def self.indexed_properties
40
+ []
41
+ end
42
+
43
+ # By default properties are empty.
44
+ def self.properties
45
+ []
46
+ end
47
+
48
+ # Returns meta-data (in the form of a hash) for the model.
49
+ def self.metadata(database)
50
+ meta = {}
51
+ meta[:count] = database.count(self)
52
+ meta[:superclass] = self.superclass.name
53
+ meta
54
+ end
55
+
56
+ # Checks if the +metadata+ are compatible with the class definition.
57
+ def self.compatible?(metadata,database)
58
+ self_metadata = self.metadata(database)
59
+ other_metadata = metadata.dup
60
+ self_metadata.delete(:count)
61
+ other_metadata.delete(:count)
62
+ self_metadata == other_metadata
63
+ end
64
+
65
+ end
66
+ end
data/lib/rod/cache.rb ADDED
@@ -0,0 +1,81 @@
1
+ require 'singleton'
2
+
3
+ module Rod
4
+ class Cache
5
+ class InternalMap
6
+ include Singleton
7
+ attr_reader :mutex
8
+
9
+ def initialize
10
+ @mutex = Mutex.new
11
+ @object_to_caches = Hash.new{|h,v| h[v] = []}
12
+ @finalizer = lambda do |object_id|
13
+ @object_to_caches[object_id].each do |cache,key|
14
+ cache.delete(key)
15
+ end
16
+ @object_to_caches.delete(object_id)
17
+ end
18
+ end
19
+
20
+ def register(value,key,cache)
21
+ @object_to_caches[value.object_id] << [cache,key]
22
+ ObjectSpace.define_finalizer(value,@finalizer)
23
+ end
24
+ end
25
+
26
+ def initialize
27
+ @map = {}
28
+ @direct_values_map = {}
29
+ end
30
+
31
+ def [](key)
32
+ if @direct_values_map[key]
33
+ return @map[key]
34
+ else
35
+ value_id = nil
36
+ value_id = @map[key]
37
+ return value_id if value_id.nil?
38
+ begin
39
+ return ObjectSpace._id2ref(value_id)
40
+ rescue RangeError
41
+ @map.delete(key)
42
+ return nil
43
+ end
44
+ end
45
+ end
46
+
47
+ def []=(key,value)
48
+ @direct_values_map.delete(key)
49
+ case value
50
+ when Fixnum, Symbol, FalseClass, TrueClass, NilClass
51
+ @map[key] = value
52
+ @direct_values_map[key] = true
53
+ else
54
+ @map[key] = value.object_id
55
+ InternalMap.instance.register(value,key,@map)
56
+ end
57
+ end
58
+
59
+ def clear
60
+ @map.clear
61
+ @direct_values_map.clear
62
+ end
63
+
64
+ def delete(key)
65
+ @map.delete(key)
66
+ @direct_values_map.delete(key)
67
+ end
68
+
69
+ def each
70
+ if block_given?
71
+ @map.each do |key,value|
72
+ begin
73
+ yield self[key]
74
+ end
75
+ end
76
+ else
77
+ enum_for(:each)
78
+ end
79
+ end
80
+ end
81
+ end
@@ -9,27 +9,35 @@ module Rod
9
9
 
10
10
  # Intializes the proxy with +size+ of the collection
11
11
  # and +fetch+ block for retrieving the object from the database.
12
- def initialize(size,&fetch)
12
+ def initialize(size,database,offset,klass)
13
13
  @size = size
14
14
  @original_size = size
15
- @fetch = fetch
15
+ @database = database
16
+ @klass = klass
17
+ @offset = offset
16
18
  @appended = []
17
- raise InvalidArgument.new("Cannot use proxy collection without a block!") unless block_given?
18
- @proxy = SimpleWeakHash.new
19
19
  end
20
20
 
21
21
  # Returns an object with given +index+.
22
22
  def [](index)
23
23
  return nil if index >= @size
24
- return @proxy[index] unless @proxy[index].nil?
25
- rod_id, klass = id_and_class_for(index)
26
- result = rod_id == 0 ? nil : klass.find_by_rod_id(rod_id)
27
- @proxy[index] = result
24
+ rod_id = id_for(index)
25
+ if rod_id.is_a?(Model)
26
+ rod_id
27
+ elsif rod_id == 0
28
+ nil
29
+ else
30
+ class_for(index).find_by_rod_id(rod_id)
31
+ end
28
32
  end
29
33
 
30
34
  # Appends element to the end of the collection.
31
- def <<(rod_id_and_class)
32
- @appended << rod_id_and_class
35
+ def <<(element)
36
+ if element.rod_id == 0
37
+ @appended << [element,element.class]
38
+ else
39
+ @appended << [element.rod_id,element.class]
40
+ end
33
41
  @size += 1
34
42
  end
35
43
 
@@ -48,7 +56,12 @@ module Rod
48
56
  def each_id
49
57
  if block_given?
50
58
  @size.times do |index|
51
- yield id_and_class_for(index)[0]
59
+ id = id_for(index)
60
+ if id.is_a?(Model)
61
+ raise IdException.new(id)
62
+ else
63
+ yield id
64
+ end
52
65
  end
53
66
  else
54
67
  enum_for(:each_id)
@@ -61,11 +74,27 @@ module Rod
61
74
  end
62
75
 
63
76
  protected
64
- def id_and_class_for(index)
77
+ def id_for(index)
65
78
  if index >= @original_size && !@appended[index - @original_size].nil?
66
- @appended[index - @original_size]
79
+ @appended[index - @original_size][0]
67
80
  else
68
- @fetch.call(index)
81
+ if @klass.nil?
82
+ @database.polymorphic_join_index(@offset,index)
83
+ else
84
+ @database.join_index(@offset,index)
85
+ end
86
+ end
87
+ end
88
+
89
+ def class_for(index)
90
+ if index >= @original_size && !@appended[index - @original_size].nil?
91
+ @appended[index - @original_size][1]
92
+ else
93
+ if @klass.nil?
94
+ Model.get_class(@database.polymorphic_join_class(@offset,index))
95
+ else
96
+ @klass
97
+ end
69
98
  end
70
99
  end
71
100
  end
data/lib/rod/constants.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Rod
2
- VERSION = "0.6.0"
2
+ VERSION = "0.6.1"
3
3
 
4
4
  # The name of file containing the data base.
5
5
  DATABASE_FILE = "database.yml"
@@ -29,4 +29,11 @@ module Rod
29
29
  :ulong => 'ULONG2NUM'
30
30
  }
31
31
 
32
+ INLINE_PATTERN_RE = /\h+\.\w+$/
33
+
34
+ LEGACY_DATA_SUFFIX = ".old"
35
+ NEW_DATA_SUFFIX = ".new"
36
+ LEGACY_MODULE = "Legacy"
37
+ LEGACY_RE = /^#{LEGACY_MODULE}::/
38
+
32
39
  end
data/lib/rod/database.rb CHANGED
@@ -146,7 +146,7 @@ module Rod
146
146
  # Returns true if the class is one of speciall classes
147
147
  # (JoinElement, PolymorphicJoinElement, StringElement).
148
148
  def special_class?(klass)
149
- self.class.special_classes.include?(klass)
149
+ self.special_classes.include?(klass)
150
150
  end
151
151
 
152
152
  #########################################################################
@@ -280,14 +280,14 @@ module Rod
280
280
  builder.c(str.margin)
281
281
 
282
282
  str =<<-END
283
- |unsigned long _allocate_join_elements(VALUE size, VALUE handler){
283
+ |unsigned long _allocate_join_elements(unsigned long size, VALUE handler){
284
284
  | _join_element * element;
285
285
  | unsigned long index;
286
286
  | #{model_struct} * model_p;
287
287
  | Data_Get_Struct(handler,#{model_struct},model_p);
288
288
  | unsigned long result = model_p->_join_element_count;
289
289
  | for(index = 0; index < size; index++){
290
- | if(model_p->_join_element_count * sizeof(_join_element) >=
290
+ | if((model_p->_join_element_count + 1) * sizeof(_join_element) >=
291
291
  | page_size() * model_p->_join_element_page_count){
292
292
  | \n#{mmap_class(JoinElement)}
293
293
  | }
@@ -302,14 +302,14 @@ module Rod
302
302
  builder.c(str.margin)
303
303
 
304
304
  str =<<-END
305
- |unsigned long _allocate_polymorphic_join_elements(VALUE size, VALUE handler){
305
+ |unsigned long _allocate_polymorphic_join_elements(unsigned long size, VALUE handler){
306
306
  | _polymorphic_join_element * element;
307
307
  | unsigned long index;
308
308
  | #{model_struct} * model_p;
309
309
  | Data_Get_Struct(handler,#{model_struct},model_p);
310
310
  | unsigned long result = model_p->_polymorphic_join_element_count;
311
311
  | for(index = 0; index < size; index++){
312
- | if(model_p->_polymorphic_join_element_count *
312
+ | if((model_p->_polymorphic_join_element_count + 1) *
313
313
  | sizeof(_polymorphic_join_element) >=
314
314
  | page_size() * model_p->_polymorphic_join_element_page_count){
315
315
  | \n#{mmap_class(PolymorphicJoinElement)}
@@ -476,7 +476,6 @@ module Rod
476
476
  | \n#{classes.map do |klass|
477
477
  <<-SUBEND
478
478
  | model_p->#{klass.struct_name}_lib_file = -1;
479
- |
480
479
  | if(model_p->#{klass.struct_name}_page_count > 0){
481
480
  | \n#{open_class_file(klass)}
482
481
  | if((model_p->#{klass.struct_name}_table = mmap(NULL,
@@ -487,7 +486,7 @@ module Rod
487
486
  | }
488
487
  | #{update_pointer(klass) unless special_class?(klass)}
489
488
  | } else {
490
- | model_p->#{klass.struct_name}_table = NULL;
489
+ | #{mmap_class(klass)}
491
490
  | }
492
491
  SUBEND
493
492
  end.join("\n")}
@@ -566,6 +565,15 @@ module Rod
566
565
  # it is rebuild on the basis of methods' signatures change.
567
566
  builder.c_singleton("void __unused_method_#{rand(1000)}(){}")
568
567
  end
568
+
569
+ # This has to be at the very end of builder definition!
570
+ self.instance_variable_set("@inline_library",builder.so_name)
571
+
572
+ # Ruby inline generated shared library.
573
+ def self.inline_library
574
+ @inline_library
575
+ end
576
+
569
577
  end
570
578
  @code_generated = true
571
579
  end
data/lib/rod/exception.rb CHANGED
@@ -53,4 +53,24 @@ module Rod
53
53
  "The value '#@value' of the #@type is invalid!"
54
54
  end
55
55
  end
56
+
57
+ # This exception is throw when an objects id is needed, but it is not
58
+ # yet stored in the database. The object might be accessed via +object+
59
+ # attribute of the exception.
60
+ class IdException < RodException
61
+ attr_reader :object
62
+
63
+ def initialize(message,object)
64
+ super("The object has not been stored in the DB and its rod_id == 0")
65
+ @object = object
66
+ end
67
+ end
68
+
69
+ # This exception is thrown if the database is not compatible with
70
+ # the library or the runtime definition of classes.
71
+ class IncompatibleVersion < RodException
72
+ def initialize(message)
73
+ super(message)
74
+ end
75
+ end
56
76
  end
@@ -1,5 +1,7 @@
1
+ require 'rod/abstract_model'
2
+
1
3
  module Rod
2
- class JoinElement
4
+ class JoinElement < AbstractModel
3
5
  def self.typedef_struct
4
6
  str = <<-END
5
7
  |typedef struct {
@@ -17,26 +19,6 @@ module Rod
17
19
  def self.struct_name
18
20
  "_join_element"
19
21
  end
20
-
21
- def self.path_for_data(path)
22
- "#{path}#{self.struct_name}.dat"
23
- end
24
-
25
- def self.page_offsets
26
- @page_offsets ||= []
27
- end
28
-
29
- def self.fields
30
- []
31
- end
32
-
33
- def self.build_structure
34
- # does nothing, the structure is not needed
35
- end
36
-
37
- def self.cache
38
- @cache ||= SimpleWeakHash.new
39
- end
40
22
  end
41
23
 
42
24
  class PolymorphicJoinElement < JoinElement
data/lib/rod/model.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  require 'rod/constants'
2
2
  require 'rod/collection_proxy'
3
+ require 'rod/abstract_model'
3
4
 
4
5
  module Rod
5
6
 
6
7
  # Abstract class representing a model entity. Each storable class has to derieve from +Model+.
7
- class Model
8
+ class Model < AbstractModel
8
9
  include ActiveModel::Validations
9
10
  extend Enumerable
10
11
 
@@ -245,43 +246,49 @@ module Rod
245
246
  unless object.is_a?(self)
246
247
  raise RodException.new("Incompatible object class #{object.class}.")
247
248
  end
248
- unless object.rod_id == 0
249
- raise RodException.new("The object #{object} is allready stored!")
250
- end
249
+ new_object = (object.rod_id == 0)
251
250
  database.store(self,object)
251
+ cache[object.rod_id] = object
252
+
253
+ referenced_objects ||= database.referenced_objects
252
254
 
253
255
  # update indices
254
- properties.each do |property,options|
255
- if options[:index]
256
- keys =
256
+ indexed_properties.each do |property,options|
257
+ # singular and plural associations with nil as value are not indexed
258
+ keys =
259
+ if new_object
257
260
  if field?(property)
258
261
  [object.send(property)]
259
262
  elsif singular_association?(property)
260
- [object.send(property).rod_id]
263
+ [object.send(property)].compact
261
264
  else
262
- object.send(property).map{|o| o.rod_id}
265
+ object.send(property).to_a.compact
263
266
  end
264
- keys.each do |key|
265
- proxy = self.index_for(property,options,key)
266
- if proxy.nil?
267
- proxy = self.set_values_for(property,options,key,0) do |index|
268
- raise RodException.new("Calling fetch block for an empty proxy!")
269
- end
270
- else
271
- unless proxy.is_a?(CollectionProxy)
272
- offset, count = proxy
273
- proxy = self.set_values_for(property,options,key,count) do |index|
274
- [database.join_index(offset,index), self]
275
- end
276
- end
267
+ elsif plural_association?(property)
268
+ object.send(property).to_a.compact
269
+ end
270
+ next if keys.nil?
271
+ keys.each.with_index do |key_or_object,key_index|
272
+ key = (key_or_object.is_a?(Model) ? key_or_object.rod_id : key_or_object)
273
+ proxy = self.index_for(property,options,key)
274
+ if proxy.nil?
275
+ proxy = self.set_values_for(property,options,key,0,database,nil)
276
+ else
277
+ unless proxy.is_a?(CollectionProxy)
278
+ offset, count = proxy
279
+ proxy = self.set_values_for(property,options,key,count,database,offset)
280
+ end
281
+ end
282
+ if new_object || plural_association?(property) && proxy[key_index].nil?
283
+ if plural_association?(property) && key == 0
284
+ # TODO #94 devise method for reference rebuilding
277
285
  end
278
- proxy << [object.rod_id,object.class]
286
+ proxy << object
279
287
  end
280
288
  end
281
289
  end
282
290
 
283
291
  # update object that references the stored object
284
- referenced_objects ||= database.referenced_objects
285
292
  # ... via singular associations
286
293
  singular_associations.each do |name, options|
287
294
  referenced = object.send(name)
@@ -323,7 +330,7 @@ module Rod
323
330
  unless reverse_references.blank?
324
331
  reverse_references.each do |referee_rod_id, method_name, class_id, index|
325
332
  referee = Model.get_class(class_id).find_by_rod_id(referee_rod_id)
326
- self.cache.send(:__get_hash__).delete(referee_rod_id)
333
+ self.cache.delete(referee_rod_id)
327
334
  if index.nil?
328
335
  # singular association
329
336
  referee.update_singular_association(method_name, object)
@@ -337,7 +344,7 @@ module Rod
337
344
  # The name of the C struct for this class.
338
345
  def self.struct_name
339
346
  return @struct_name unless @struct_name.nil?
340
- name = self.to_s.underscore.gsub(/\//,"__")
347
+ name = struct_name_for(self.to_s)
341
348
  unless name =~ /^\#/
342
349
  # not an anonymous class
343
350
  @struct_name = name
@@ -345,6 +352,11 @@ module Rod
345
352
  name
346
353
  end
347
354
 
355
+ # Returns the struct name for the class +name+.
356
+ def self.struct_name_for(name)
357
+ name.underscore.gsub(/\//,"__")
358
+ end
359
+
348
360
  # Finder for rod_id.
349
361
  def self.find_by_rod_id(rod_id)
350
362
  if rod_id <= 0 || rod_id > self.count
@@ -380,6 +392,109 @@ module Rod
380
392
  end
381
393
  end
382
394
 
395
+ # Metadata for the model class.
396
+ def self.metadata(database)
397
+ meta = super(database)
398
+ # fields
399
+ fields = meta[:fields] = {} unless self.fields.size == 1
400
+ self.fields.each do |field,options|
401
+ next if field == "rod_id"
402
+ fields[field] = {}
403
+ fields[field][:options] = options
404
+ end
405
+ # singular_associations
406
+ has_one = meta[:has_one] = {} unless self.singular_associations.empty?
407
+ self.singular_associations.each do |name,options|
408
+ has_one[name] = {}
409
+ has_one[name][:options] = options
410
+ end
411
+ # plural_associations
412
+ has_many = meta[:has_many] = {} unless self.plural_associations.empty?
413
+ self.plural_associations.each do |name,options|
414
+ has_many[name] = {}
415
+ has_many[name][:options] = options
416
+ end
417
+ meta
418
+ end
419
+
420
+ # Generates the model class based on the metadata and places
421
+ # it in the +module_instance+ or Object (default scope) if module is nil.
422
+ def self.generate_class(class_name,metadata)
423
+ superclass = metadata[:superclass].constantize
424
+ namespace = define_context(class_name)
425
+ klass = Class.new(superclass)
426
+ namespace.const_set(class_name.split("::")[-1],klass)
427
+ [:fields,:has_one,:has_many].each do |type|
428
+ (metadata[type] || []).each do |name,options|
429
+ if type == :fields
430
+ internal_options = options[:options].dup
431
+ field_type = internal_options.delete(:type)
432
+ klass.send(:field,name,field_type,internal_options)
433
+ else
434
+ klass.send(type,name,options[:options])
435
+ end
436
+ end
437
+ end
438
+ klass
439
+ end
440
+
441
+ # Migrates the class to the new model, i.e. it copies all the
442
+ # values of properties that both belong to the class in the old
443
+ # and the new model.
444
+ def self.migrate
445
+ new_class = self.name.sub(LEGACY_RE,"").constantize
446
+ old_object = self.new
447
+ new_object = new_class.new
448
+ puts "Migrating #{new_class}" if $ROD_DEBUG
449
+ self.properties.each do |name,options|
450
+ next unless new_class.properties.keys.include?(name)
451
+ print "- #{name}... " if $ROD_DEBUG
452
+ if options.map{|k,v| [k,v.to_s.sub(LEGACY_RE,"")]} !=
453
+ new_class.properties[name].map{|k,v| [k,v.to_s]}
454
+ raise IncompatibleVersion.
455
+ new("Incompatible definition of property '#{name}'\n" +
456
+ "Definition is different in the old and "+
457
+ "the new schema for '#{new_class}':\n" +
458
+ " #{options} \n" +
459
+ " #{new_class.properties[name]}")
460
+ end
461
+ if self.field?(name)
462
+ if self.string_field?(options[:type])
463
+ self.count.times do |position|
464
+ new_object.send("_#{name}_length=",position+1,
465
+ old_object.send("_#{name}_length",position+1))
466
+ new_object.send("_#{name}_offset=",position+1,
467
+ old_object.send("_#{name}_offset",position+1))
468
+ end
469
+ else
470
+ self.count.times do |position|
471
+ new_object.send("_#{name}=",position + 1,
472
+ old_object.send("_#{name}",position + 1))
473
+ end
474
+ end
475
+ elsif self.singular_association?(name)
476
+ self.count.times do |position|
477
+ new_object.send("_#{name}=",position + 1,
478
+ old_object.send("_#{name}",position + 1))
479
+ end
480
+ if options[:polymorphic]
481
+ self.count.times do |position|
482
+ new_object.send("_#{name}__class=",position + 1,
483
+ old_object.send("_#{name}__class",position + 1))
484
+ end
485
+ end
486
+ else
487
+ self.count.times do |position|
488
+ new_object.send("_#{name}_count=",position + 1,
489
+ old_object.send("_#{name}_count",position + 1))
490
+ new_object.send("_#{name}_offset=",position + 1,
491
+ old_object.send("_#{name}_offset",position + 1))
492
+ end
493
+ end
494
+ puts "done" if $ROD_DEBUG
495
+ end
496
+ end
497
+
383
498
  protected
384
499
  # The pointer to the mmaped table of C structs.
385
500
  def self.rod_pointer
@@ -480,7 +595,7 @@ module Rod
480
595
  # The object cache of this class.
481
596
  # XXX consider moving it to the database.
482
597
  def self.cache
483
- @cache ||= SimpleWeakHash.new
598
+ @cache ||= Cache.new
484
599
  end
485
600
 
486
601
  # The module context of the class.
@@ -498,6 +613,22 @@ module Rod
498
613
  end
499
614
  end
500
615
 
616
+ # Defines the namespace (contex) for given +class_name+ - if the constants
617
+ # (modules and classes) are defined, they are just digged into,
618
+ # if not - they are defined as modules.
619
+ def self.define_context(class_name)
620
+ class_name.split("::")[0..-2].inject(Object) do |mod,segment|
621
+ begin
622
+ mod.const_get(segment,false)
623
+ rescue NameError
624
+ new_mod = Module.new
625
+ mod.const_set(segment,new_mod)
626
+ new_mod
627
+ end
628
+ end
629
+ end
630
+
631
+
501
632
  #########################################################################
502
633
  # DB-oriented API
503
634
  #########################################################################
@@ -515,10 +646,23 @@ module Rod
515
646
  to_s.to_i(16) % 2 ** 32
516
647
  end
517
648
 
649
+ # Allows for setting arbitrary name for the model path, i.e.
650
+ # the model-specific fragment of the path to the model files.
651
+ # By default it is the same as Model.struct_name
652
+ def self.model_path=(path)
653
+ @model_path = path
654
+ end
655
+
656
+ # Returns the model path, i.e. the model-specific fragment of
657
+ # the path to the model files (data, indices, etc.).
658
+ def self.model_path
659
+ @model_path || self.struct_name
660
+ end
661
+
518
662
  # The name of the file (for given +relative_path+), which the data of this class
519
663
  # is stored in.
520
664
  def self.path_for_data(relative_path)
521
- "#{relative_path}#{self.struct_name}.dat"
665
+ "#{relative_path}#{model_path}.dat"
522
666
  end
523
667
 
524
668
  # The name of the file or directory (for given +relative_path+), which the
@@ -526,9 +670,9 @@ module Rod
526
670
  def self.path_for_index(relative_path,field,options)
527
671
  case options[:index]
528
672
  when :flat,true
529
- "#{relative_path}#{self.struct_name}_#{field}.idx"
673
+ "#{relative_path}#{model_path}_#{field}.idx"
530
674
  when :segmented
531
- "#{relative_path}#{self.struct_name}_#{field}_idx/"
675
+ "#{relative_path}#{model_path}_#{field}_idx/"
532
676
  else
533
677
  raise RodException.new("Invalid index type #{type}")
534
678
  end
@@ -675,6 +819,22 @@ module Rod
675
819
  field_writer("#{name}_count","unsigned long",builder)
676
820
  field_writer("#{name}_offset","unsigned long",builder)
677
821
  end
822
+
823
+ str=<<-END
824
+ |unsigned int struct_size(){
825
+ | return sizeof(#{self.struct_name});
826
+ |}
827
+ END
828
+
829
+ builder.c_singleton(str.margin)
830
+
831
+ # This has to be the last position in the builder!
832
+ self.instance_variable_set("@inline_library",builder.so_name)
833
+
834
+ # Ruby inline generated shared library.
835
+ def self.inline_library
836
+ @inline_library
837
+ end
678
838
  end
679
839
 
680
840
  ## accessors for fields, plural and singular relationships follow
@@ -789,7 +949,7 @@ module Rod
789
949
  else
790
950
  "#{self.scope_name}::#{::English::Inflect.singular(name).camelcase}"
791
951
  end
792
- klass = class_name.constantize unless options[:polymorphic]
952
+ klass = options[:polymorphic] ? nil : class_name.constantize
793
953
 
794
954
  # getter
795
955
  define_method("#{name}") do
@@ -802,17 +962,7 @@ module Rod
802
962
  end
803
963
  return instance_variable_set("@#{name}",[]) if count == 0
804
964
  offset = self.send("_#{name}_offset",@rod_id)
805
- unless options[:polymorphic]
806
- proxy = CollectionProxy.new(count) do |index|
807
- [database.join_index(offset,index), klass]
808
- end
809
- else
810
- proxy = CollectionProxy.new(count) do |index|
811
- rod_id = database.polymorphic_join_index(offset,index)
812
- class_id = database.polymorphic_join_class(offset,index)
813
- [rod_id, rod_id == 0 ? 0 : Model.get_class(class_id)]
814
- end
815
- end
965
+ proxy = CollectionProxy.new(count,database,offset,klass)
816
966
  instance_variable_set("@#{name}", proxy)
817
967
  end
818
968
  proxy
@@ -834,24 +984,31 @@ module Rod
834
984
  end
835
985
 
836
986
  # indices
837
- properties.each do |property,options|
987
+ indexed_properties.each do |property,options|
838
988
  # optimization
839
989
  property = property.to_s
840
- if options[:index]
841
- (class << self; self; end).class_eval do
842
- # Find all objects with given +value+ of the +property+.
843
- define_method("find_all_by_#{property}") do |value|
844
- value = value.rod_id if value.is_a?(Model)
845
- offset,count = index_for(property,options,value)
990
+ (class << self; self; end).class_eval do
991
+ # Find all objects with given +value+ of the +property+.
992
+ define_method("find_all_by_#{property}") do |value|
993
+ value = value.rod_id if value.is_a?(Model)
994
+ proxy = index_for(property,options,value)
995
+ if proxy.is_a?(CollectionProxy)
996
+ proxy
997
+ else
998
+ offset,count = proxy
846
999
  return [] if offset.nil?
847
- CollectionProxy.new(count) do |index|
848
- [database.join_index(offset,index),self]
849
- end
1000
+ CollectionProxy.new(count,database,offset,self)
850
1001
  end
1002
+ end
851
1003
 
852
- # Find first object with given +value+ of the +property+.
853
- define_method("find_by_#{property}") do |value|
854
- offset,count = index_for(property,options)[value]
1004
+ # Find first object with given +value+ of the +property+.
1005
+ define_method("find_by_#{property}") do |value|
1006
+ value = value.rod_id if value.is_a?(Model)
1007
+ proxy = index_for(property,options)[value]
1008
+ if proxy.is_a?(CollectionProxy)
1009
+ proxy[0]
1010
+ else
1011
+ offset,count = proxy
855
1012
  if offset.nil?
856
1013
  nil
857
1014
  else
@@ -870,6 +1027,11 @@ module Rod
870
1027
  self.fields.merge(self.singular_associations.merge(self.plural_associations))
871
1028
  end
872
1029
 
1030
+ # Returns (and caches) only properties which are indexed.
1031
+ def indexed_properties
1032
+ @indexed_properties ||= self.properties.select{|p,o| o[:index]}
1033
+ end
1034
+
873
1035
  # Returns true if the +name+ (as symbol) is a name of a field.
874
1036
  def field?(name)
875
1037
  self.fields.keys.include?(name)
@@ -905,8 +1067,8 @@ module Rod
905
1067
  # the particular +key+. Method expects +fetch+ block and
906
1068
  # creates a CollectionProxy based on that block.
907
1069
  # The size of the collection is given as +count+.
908
- def set_values_for(property,options,key,count,&fetch)
909
- index_for(property,options)[key] = CollectionProxy.new(count,&fetch)
1070
+ def set_values_for(property,options,key,count,database,offset)
1071
+ index_for(property,options)[key] = CollectionProxy.new(count,database,offset,self)
910
1072
  end
911
1073
 
912
1074
  private