Rubernate 0.1.0

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,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