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.
- data/README +30 -0
- data/db/mysql.sql +35 -0
- data/db/oracle.sql +29 -0
- data/lib/rubernate/callbacks.rb +70 -0
- data/lib/rubernate/entity.rb +85 -0
- data/lib/rubernate/impl/dbigeneric.rb +286 -0
- data/lib/rubernate/impl/dbimysql.rb +28 -0
- data/lib/rubernate/impl/dbioracle.rb +29 -0
- data/lib/rubernate/impl/memory.rb +147 -0
- data/lib/rubernate/mixins.rb +91 -0
- data/lib/rubernate/peer.rb +70 -0
- data/lib/rubernate/queries.rb +444 -0
- data/lib/rubernate/runtime.rb +215 -0
- data/lib/rubernate.rb +127 -0
- data/tests/README +16 -0
- data/tests/all_tests.rb +12 -0
- data/tests/config.rb +34 -0
- data/tests/rubernate/callbacks_test.rb +120 -0
- data/tests/rubernate/fixtures.rb +27 -0
- data/tests/rubernate/impl/dbigeneric_stub.rb +635 -0
- data/tests/rubernate/impl/dbimysql_test.rb +19 -0
- data/tests/rubernate/impl/dbioracle_test.rb +19 -0
- data/tests/rubernate/impl/memory_test.rb +188 -0
- data/tests/rubernate/queries_test.rb +176 -0
- data/tests/rubernate/rubernate_test.rb +326 -0
- data/tests/rubernate/utils_test.rb +42 -0
- metadata +74 -0
@@ -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
|