Rubernate 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,147 @@
1
+ require 'rubernate/queries'
2
+
3
+ module Rubernate
4
+ module Memory
5
+ module Queries
6
+ class Query < Rubernate::Queries::Generic::Expr
7
+ def params v
8
+ case v
9
+ when Array: v
10
+ when Hash: v.values
11
+ else [v]
12
+ end
13
+ end
14
+ end
15
+ class Factory < Rubernate::Queries::Factory
16
+ def initialize
17
+ super
18
+ @query = Query
19
+ end
20
+ end
21
+ end
22
+ class Reference
23
+ attr_reader :primary_key, :object_class
24
+ def initialize pk, klass
25
+ @primary_key, @object_class = pk, klass
26
+ end
27
+
28
+ def == other
29
+ @primary_key == other.primary_key and
30
+ @object_class == other.object_class
31
+ end
32
+ end
33
+
34
+ class RuntimeFactory
35
+ def initialize
36
+ @pk, @database = 1001, {}
37
+ end
38
+ def pk_next
39
+ @pk+= 1
40
+ end
41
+ def create
42
+ Runtime.new @database, self
43
+ end
44
+ end
45
+
46
+ class Runtime < Rubernate::Runtime
47
+ attr :pk, true
48
+
49
+ # Database is maps {pk => [class, {key => value}]}
50
+ def initialize database, pk_seq
51
+ super()
52
+ @pk_seq, @database, @factory = pk_seq, database, Queries::Factory.new
53
+ end
54
+
55
+ # Loads object by primary_key
56
+ def load_by_pk pk
57
+ klass, data = @database[pk]
58
+ return nil unless data
59
+ object = instantiate pk, klass, load_peer(data)
60
+ post_load object
61
+ object
62
+ end
63
+
64
+ # Loads objects by query
65
+ def load_by_query query, params=[]
66
+ result = []
67
+ if query =~ /\s*all\s*/
68
+ result = @database.keys
69
+ else
70
+ result = query.split.collect! {|pk_str| pk_str.to_i} * @database.keys
71
+ end
72
+ result.collect! {|pk| load_by_pk pk}
73
+ end
74
+
75
+ def load_peer data
76
+ peer = Rubernate::Peer.new
77
+ data.each_pair {|p_name, p_data| peer[p_name] = load_param p_data}
78
+ peer
79
+ end
80
+
81
+ def load_param p_data
82
+ case p_data
83
+ when Reference:
84
+ instantiate p_data.primary_key, p_data.object_class
85
+ when Array:
86
+ p_data.collect{|ref| instantiate(ref.primary_key, ref.object_class)}
87
+ when Hash:
88
+ result = {}
89
+ p_data.each_pair {|key, ref|
90
+ result[key] = instantiate(ref.primary_key, ref.object_class)
91
+ }
92
+ result
93
+ when Fixnum
94
+ p_data
95
+ else p_data.dup
96
+ end
97
+ end
98
+
99
+ # Saves objects and clears dirty flag
100
+ def save objects
101
+ objects = [objects] unless objects.kind_of? Array
102
+ for object in objects
103
+ @database[object.primary_key] = [object.class, save_peer(object.peer)]
104
+ object.peer.dirty = false
105
+ end
106
+ end
107
+
108
+ def save_peer peer
109
+ result = {}
110
+ peer.each {|key, value| result[key] = save_param value }
111
+ result
112
+ end
113
+
114
+ def save_param value
115
+ case value
116
+ when Entity:
117
+ Reference.new value.primary_key, value.class
118
+ when Array:
119
+ value.collect{ |item| Reference.new item.primary_key, item.class }
120
+ when Hash:
121
+ result = {}
122
+ value.each {|key, value|
123
+ result[key] = Reference.new value.primary_key, value.class
124
+ }
125
+ result
126
+ when Fixnum:
127
+ value
128
+ else
129
+ value.dup
130
+ end
131
+ end
132
+
133
+ # Creates object. Object must have peer.
134
+ def create object
135
+ object.primary_key = @pk_seq.pk_next
136
+ save object
137
+ object.primary_key
138
+ end
139
+
140
+ # Deletes object
141
+ def delete object
142
+ @database.delete object.primary_key
143
+ object.primary_key = nil
144
+ end
145
+ end
146
+ end
147
+ end
@@ -0,0 +1,91 @@
1
+ require 'set'
2
+
3
+ class Module
4
+ # Finds class defined in current module.
5
+ # :call-seq:
6
+ # Module.find_class 'ClassName' # finds class 'ClassName' in global space
7
+ # SomeModule.find_class 'ClassName' # finds class 'ClassName' in module SomeModule
8
+ #
9
+ def find_class name, scaned = Set.new
10
+ scaned << self
11
+ for unit in module_units - scaned
12
+ return unit if unit.name == name
13
+ result = unit.find_class name, scaned
14
+ return result if result
15
+ end
16
+ nil
17
+ end
18
+
19
+ # Iterates over all classes.
20
+ def find_all scaned = Set.new, &filter # :yeild: class
21
+ scaned << self
22
+ result = []
23
+ for unit in module_units - scaned
24
+ if unit.is_a? Class
25
+ result << unit if yield unit
26
+ end
27
+ result.concat unit.find_all(scaned, &filter)
28
+ end
29
+ return result
30
+ end
31
+ private
32
+ # Returns all modules and classes defined inside this module
33
+ def module_units
34
+ constants.collect { |name| const_get name }.
35
+ find_all { |const|
36
+ const.class == Class or
37
+ const.class == Module
38
+ }.to_set
39
+ end
40
+ end
41
+
42
+ class Class
43
+ # Creates persistent property in a class
44
+ # The class MUST have an empty constructor.
45
+ def persistent *props
46
+ include Rubernate::Entity
47
+ for prop in props
48
+ define_accessor prop
49
+ end
50
+ end
51
+
52
+ # Returns subclasses names (self is not included)
53
+ attr :subclasses
54
+
55
+ # Register +klass+ as descendant in all superclasses
56
+ def update_superclass klass
57
+ @subclasses ||= []
58
+ @subclasses << klass.name if klass != self
59
+ superclass.update_superclass klass if superclass
60
+ end
61
+
62
+ # Defines accessor for persistent property. Should not be used by users
63
+ def define_accessor name
64
+ module_eval %{
65
+ def #{name}
66
+ ensure_loaded
67
+ peer[:#{name}]
68
+ end
69
+
70
+ def #{name}= value
71
+ ensure_loaded
72
+ peer.set :#{name}, value, &on_change_callback
73
+ end
74
+ }
75
+ end
76
+ private :persistent, :define_accessor
77
+
78
+ alias old_inherited inherited
79
+ # Collect subclasses
80
+ def inherited klass
81
+ old_inherited klass
82
+ update_superclass klass
83
+ end
84
+
85
+ # Update classes already loaded
86
+ Module.find_all do |klass|
87
+ klass.update_superclass klass
88
+ end
89
+ # Hide update_superclass
90
+ protected :update_superclass
91
+ end
@@ -0,0 +1,70 @@
1
+ module Rubernate
2
+ # Represent objects peer - properties holder.
3
+ class Peer < Hash
4
+ # Sets peers dirty flag to specified value.
5
+ # And resets dirty flag for all properites which stored in this peer.
6
+ def dirty= f_dirty
7
+ @dirty = f_dirty
8
+ for value in values
9
+ value.memorize! if value.is_a? Container
10
+ end
11
+ end
12
+
13
+ # If peers dirty flag set to true returns it immediately
14
+ # else iterate over all properties and returns true if dirty
15
+ # property apperas. +on_modify+ callback invoked if dirty is +true+.
16
+ def dirty? &on_modify
17
+ return true if @dirty
18
+ f_dirty, name, value, old_value = false
19
+ each do |name, value|
20
+ if value.is_a? Container
21
+ old_value = value.recall
22
+ if old_value
23
+ f_dirty = dirty = true
24
+ break
25
+ end
26
+ end
27
+ end
28
+ yield name, old_value, value if block_given? and f_dirty
29
+ f_dirty
30
+ end
31
+
32
+ alias old_set []=
33
+
34
+ def []= key, value
35
+ set key, value
36
+ end
37
+
38
+ def set key, value, &on_change
39
+ old_value = self[key]
40
+ return if old_value == value
41
+ @dirty = true
42
+ if value.is_a? Array or value.is_a? Hash
43
+ value.extend Container
44
+ end
45
+ if value
46
+ old_set key, value
47
+ else
48
+ delete key
49
+ end
50
+ yield key, old_value, value if block_given?
51
+ end
52
+
53
+ # This module is for including to collections of Persistent
54
+ # objects. It adds fied that contains copy of collection
55
+ # and controls dirty status of collection.
56
+ module Container
57
+ attr_writer :copy
58
+
59
+ # Remember state of collection
60
+ def memorize!
61
+ @copy = dup
62
+ end
63
+
64
+ # Returns remembered state of collection if it different now
65
+ def recall
66
+ return self != @copy ? @copy : nil
67
+ end
68
+ end
69
+ end
70
+ end