rod 0.6.0 → 0.6.1

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.
@@ -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