Rubernate 0.1.5 → 0.1.6
Sign up to get free protection for your applications and to get access to all the features.
- data/README +1 -1
- data/lib/rubernate/callbacks.rb +17 -20
- data/lib/rubernate/impl/dbi_generic.rb +77 -50
- data/lib/rubernate/impl/dbi_mysql.rb +2 -2
- data/lib/rubernate/impl/dbi_oracle.rb +29 -3
- data/lib/rubernate/impl/dbi_pg.rb +2 -2
- data/lib/rubernate/impl/memory.rb +14 -12
- data/lib/rubernate/init/init_mysql.rb +2 -2
- data/lib/rubernate/init/init_oracle.rb +2 -2
- data/lib/rubernate/lazyload.rb +63 -0
- data/lib/rubernate/mixins.rb +6 -4
- data/lib/rubernate/persistent.rb +21 -21
- data/lib/rubernate/queries.rb +33 -11
- data/lib/rubernate/runtime.rb +49 -27
- data/lib/rubernate.rb +51 -33
- data/tests/config.rb +8 -5
- data/tests/rubernate/impl/dbi_generic_stub.rb +102 -35
- data/tests/rubernate/impl/dbi_oracle_test.rb +20 -0
- data/tests/rubernate/impl/memory_test.rb +16 -17
- data/tests/rubernate/rubernate_test.rb +352 -302
- metadata +3 -2
data/lib/rubernate/queries.rb
CHANGED
@@ -48,7 +48,13 @@ module Queries
|
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
|
-
# Factory method, creates queries
|
51
|
+
# Factory method, creates queries using stardart factory.
|
52
|
+
#
|
53
|
+
# :call-seq:
|
54
|
+
#
|
55
|
+
# q = Rubernate::Queries.query "Select :o; Where o.prop.str == :value"
|
56
|
+
# puts q.to_sql
|
57
|
+
#
|
52
58
|
def self.query q_text=nil, &q_block
|
53
59
|
if block_given?
|
54
60
|
@@generic_factory.query(&q_block)
|
@@ -68,7 +74,7 @@ module Queries
|
|
68
74
|
#
|
69
75
|
# New implementations of elements MUST accept factory as it's first parameter.
|
70
76
|
class Factory
|
71
|
-
attr_reader :cache # Queries cache. TODO: limit size!
|
77
|
+
attr_reader :cache # Queries cache. TODO: limit cache size!
|
72
78
|
# Initalizes default implementations
|
73
79
|
def initialize
|
74
80
|
@expr = Expr
|
@@ -102,7 +108,6 @@ module Queries
|
|
102
108
|
end
|
103
109
|
end
|
104
110
|
|
105
|
-
# Contains classes for standart ANSY SQL.
|
106
111
|
# Represent abstract expression
|
107
112
|
class Expr
|
108
113
|
include Operations
|
@@ -145,7 +150,7 @@ module Queries
|
|
145
150
|
end
|
146
151
|
end
|
147
152
|
|
148
|
-
# Represents constraint for one
|
153
|
+
# Represents constraint for one column.
|
149
154
|
class UnOpConstr < Expr
|
150
155
|
def initialize factory, expr, op, braces = false
|
151
156
|
@factory, @op, @braces = factory, op, braces
|
@@ -169,7 +174,7 @@ module Queries
|
|
169
174
|
end
|
170
175
|
end
|
171
176
|
|
172
|
-
# Represent constraint
|
177
|
+
# Represent constraint applied on two columns.
|
173
178
|
class BinOpConstr < Expr
|
174
179
|
def initialize factory, expr1, expr2, sign, braces = false
|
175
180
|
@factory, @sign, @braces = factory, sign, braces
|
@@ -196,7 +201,8 @@ module Queries
|
|
196
201
|
end
|
197
202
|
end
|
198
203
|
|
199
|
-
# Represent expression with
|
204
|
+
# Represent expression with tabls field such as
|
205
|
+
# <tt>r_params.name</tt> or <tt>r_objects.object_class</tt>
|
200
206
|
class FieldExpr < Expr
|
201
207
|
def initialize factory, table, field
|
202
208
|
@factory, @table, @field, @markers = factory, table, field, []
|
@@ -216,7 +222,7 @@ module Queries
|
|
216
222
|
end
|
217
223
|
end
|
218
224
|
|
219
|
-
#
|
225
|
+
# Represents r_params for hashes and arrays constrained by key
|
220
226
|
class KeyRefExpr < BinOpConstr
|
221
227
|
def initialize factory, r_param, key_field, key_value
|
222
228
|
@factory, @r_param, @key_field, @key_value = factory, r_param, key_field, key_value
|
@@ -227,7 +233,7 @@ module Queries
|
|
227
233
|
end
|
228
234
|
end
|
229
235
|
|
230
|
-
# Reresents
|
236
|
+
# Reresents collection of expressions
|
231
237
|
class ExprsList < Expr
|
232
238
|
def initialize factory, list
|
233
239
|
@factory, @exprs = factory, list.collect{|expr| Expr === expr ? expr : factory.expr(expr)}
|
@@ -243,7 +249,7 @@ module Queries
|
|
243
249
|
end
|
244
250
|
end
|
245
251
|
|
246
|
-
# Represents r_params table
|
252
|
+
# Represents <tt>r_params</tt> table
|
247
253
|
class RParam < Expr
|
248
254
|
include Rubernate::DBI
|
249
255
|
|
@@ -260,15 +266,31 @@ module Queries
|
|
260
266
|
@r_object.to_sql + @p_name
|
261
267
|
end
|
262
268
|
|
263
|
-
#
|
269
|
+
# Creates accessor to +r_params.primary_key+.
|
264
270
|
def pk () f_expr 'object_pk'; end
|
271
|
+
|
272
|
+
# Creates accessor to +r_params.int_value+.
|
265
273
|
def int () f_expr 'int_value'; end
|
274
|
+
|
275
|
+
# Creates accessor to +r_parms.flt_value+.
|
266
276
|
def float() f_expr 'flt_value'; end
|
277
|
+
|
278
|
+
# Creates accessor to +r_parms.str_value+.
|
267
279
|
def str () f_expr 'str_value'; end
|
280
|
+
|
281
|
+
# Creates accessor to +r_parms.dat_value+ with appropriate converison to time if needed.
|
268
282
|
def time () f_expr 'dat_value'; end
|
283
|
+
|
284
|
+
# Creates accessor to +r_parms.dat_value+ with appropriate converison if needed.
|
269
285
|
def date () f_expr 'dat_value'; end
|
286
|
+
|
287
|
+
# Creates accessor to +r_parms.ref_value+.
|
270
288
|
def ref () f_expr 'ref_value'; end
|
289
|
+
|
290
|
+
# Creates accessor to +r_parms.flags+.
|
271
291
|
def flags() f_expr 'flags'; end
|
292
|
+
|
293
|
+
# Creates accessor to +r_parms.name+.
|
272
294
|
def name () f_expr 'name'; end
|
273
295
|
|
274
296
|
# The following methods checks r_param.flags value. (r_param.flags)
|
@@ -304,7 +326,7 @@ module Queries
|
|
304
326
|
end
|
305
327
|
end
|
306
328
|
|
307
|
-
# Represent r_objects table
|
329
|
+
# Represent <tt>r_objects</tt> table
|
308
330
|
class RObject < Expr
|
309
331
|
# Init Table accepts query and name of table
|
310
332
|
def initialize factory, name
|
data/lib/rubernate/runtime.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
require 'rubernate/lazyload'
|
2
|
+
|
3
|
+
module Rubernate
|
2
4
|
# Base class for all Runtime implementations.
|
3
5
|
class Runtime
|
4
6
|
include Callbacks::Runtime
|
@@ -6,13 +8,18 @@ module Rubernate
|
|
6
8
|
# Log for Runtime events
|
7
9
|
Log = Rubernate::Log
|
8
10
|
|
11
|
+
# Runtime scope settings
|
12
|
+
attr_reader :settings
|
13
|
+
|
9
14
|
def initialize
|
10
15
|
@pool = {} # Contains objects loaded during the session
|
11
|
-
|
12
|
-
|
16
|
+
@@factory = Queries::Factory.new unless @@factory
|
17
|
+
@factory = @@factory
|
18
|
+
@settings = Hash.new {|hash, key| hash[key] = Rubernate.settings[key]}
|
19
|
+
end
|
20
|
+
@@factory = nil
|
13
21
|
|
14
|
-
# Finds object by primary key
|
15
|
-
# raises ObjectNotFoundException if object is not found
|
22
|
+
# Finds object by primary key. Raises ObjectNotFoundException if object is not found
|
16
23
|
def find_by_pk pk, load = false
|
17
24
|
result = @pool[pk]
|
18
25
|
unless result
|
@@ -28,13 +35,14 @@ module Rubernate
|
|
28
35
|
result
|
29
36
|
end
|
30
37
|
|
31
|
-
# Finds objects by query. Returns ordered list of objects.
|
32
|
-
#
|
33
|
-
#
|
38
|
+
# Finds objects by query. Returns ordered list of objects or empty one if no objects was found.
|
39
|
+
# The query can be one of two types.
|
40
|
+
# * native sql - in this case +params+ should be of type Array.
|
41
|
+
# * query language - +params+ shoud be of type Hash.
|
34
42
|
def find_by_query query, params={} #TODO: improve working with paramters
|
35
43
|
flush_modified
|
36
44
|
params = case params
|
37
|
-
when Hash: params
|
45
|
+
when Hash : params
|
38
46
|
when Array: params
|
39
47
|
else [params]
|
40
48
|
end
|
@@ -54,7 +62,7 @@ module Rubernate
|
|
54
62
|
# primary key already loaded in session.
|
55
63
|
def attach object
|
56
64
|
raise "Can't attach object: #{object.primary_key}" if @pool.has_key? object.primary_key
|
57
|
-
object.
|
65
|
+
object.__peer = Peer.new unless object.__peer
|
58
66
|
create object
|
59
67
|
@pool[object.primary_key] = object
|
60
68
|
Log.debug {"Attach #{object.class.name}: #{object.primary_key} to session"}
|
@@ -75,13 +83,13 @@ module Rubernate
|
|
75
83
|
raise
|
76
84
|
end
|
77
85
|
|
78
|
-
# Begins session
|
86
|
+
# Begins session and transaction.
|
79
87
|
def begin
|
80
88
|
Log.debug {"Begin session #{self}"}
|
81
89
|
on_begin
|
82
90
|
end
|
83
91
|
|
84
|
-
# Persist all changes and
|
92
|
+
# Persist all changes and commit transction.
|
85
93
|
def commit
|
86
94
|
before_commit # callback
|
87
95
|
flush_modified
|
@@ -93,7 +101,7 @@ module Rubernate
|
|
93
101
|
raise
|
94
102
|
end
|
95
103
|
|
96
|
-
# Discard all changes and
|
104
|
+
# Discard all changes and rollback transaction.
|
97
105
|
def rollback ex='not specified'
|
98
106
|
on_rollback # callback
|
99
107
|
Log.warn {"Rollback session #{self} due to error: #{ex}"}
|
@@ -103,7 +111,10 @@ module Rubernate
|
|
103
111
|
raise
|
104
112
|
end
|
105
113
|
|
106
|
-
#
|
114
|
+
# Stores modified objects to database. This method is invoked before
|
115
|
+
# transaction commit and before each invokation of Runtime.find_by_query.
|
116
|
+
# You can invoke this method manually if you want to store all changes of your
|
117
|
+
# objects to database immediately.
|
107
118
|
def flush_modified
|
108
119
|
objects = modified
|
109
120
|
before_flush modified # callback
|
@@ -116,8 +127,9 @@ module Rubernate
|
|
116
127
|
|
117
128
|
private
|
118
129
|
# Creates new object instance or returns insance from pool
|
119
|
-
#
|
120
|
-
|
130
|
+
# * klass - can be Class or String (class name)
|
131
|
+
# * peer - can be Peer, LazyLoader or nill
|
132
|
+
def instantiate pk, klass, peer = nil
|
121
133
|
object = @pool[pk]
|
122
134
|
unless object
|
123
135
|
clazz = klass.is_a?(Class) ? klass : get_class(klass)
|
@@ -125,10 +137,10 @@ module Rubernate
|
|
125
137
|
object.primary_key = pk
|
126
138
|
@pool[pk] = object
|
127
139
|
end
|
128
|
-
object.
|
140
|
+
object.__peer = peer if peer
|
129
141
|
object
|
130
|
-
end
|
131
|
-
|
142
|
+
end
|
143
|
+
|
132
144
|
#Classes cache.
|
133
145
|
@@classes = {}
|
134
146
|
|
@@ -141,17 +153,27 @@ module Rubernate
|
|
141
153
|
@@classes[name] = klass
|
142
154
|
end
|
143
155
|
|
156
|
+
# Returns LazyLoaderFactory instance.
|
157
|
+
def lazy_factory
|
158
|
+
case settings[:lazy_load]
|
159
|
+
when :holder: HolderLazyLoaderFactory.new
|
160
|
+
when :collection: ParamLazyLoaderFactory.new
|
161
|
+
when :separately, nil: LazyLoaderFactory.new
|
162
|
+
else "wrong value for setting :lazy_load - #{settings[:lazy_load]}"
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
144
166
|
# Returns modified objects.
|
145
167
|
def modified
|
146
|
-
@pool.values.find_all {|object| object.
|
168
|
+
@pool.values.find_all {|object| object.__peer and object.dirty?}
|
147
169
|
end
|
148
170
|
|
149
|
-
# Callback functions invoked when session successfully closed
|
171
|
+
# Callback functions it's invoked when session successfully closed
|
150
172
|
# Should be overriden by subclasses to perform implement closing logic
|
151
173
|
def close
|
152
174
|
end
|
153
175
|
|
154
|
-
# Callback functions invoked when session fails
|
176
|
+
# Callback functions it's invoked when session fails.
|
155
177
|
# Should be overriden by subclasses to cleanup resources
|
156
178
|
def failed
|
157
179
|
end
|
@@ -203,10 +225,10 @@ module Rubernate
|
|
203
225
|
end
|
204
226
|
|
205
227
|
def post_load object
|
206
|
-
for value in object.
|
228
|
+
for value in object.__peer.values
|
207
229
|
value.compact! if value.is_a? Array
|
208
230
|
end
|
209
|
-
object.
|
231
|
+
object.__peer.dirty = false
|
210
232
|
object.on_load
|
211
233
|
end
|
212
234
|
|
@@ -221,10 +243,10 @@ module Rubernate
|
|
221
243
|
when Class: p.name
|
222
244
|
else p
|
223
245
|
end
|
224
|
-
end
|
246
|
+
end
|
225
247
|
end
|
226
|
-
|
227
|
-
# Represetns
|
248
|
+
|
249
|
+
# Represetns object whose class wasn't found during loading.
|
228
250
|
class DummyPersistent
|
229
251
|
persistent
|
230
252
|
def initialize class_name
|
data/lib/rubernate.rb
CHANGED
@@ -41,8 +41,8 @@ module Rubernate
|
|
41
41
|
# Rubernate core singelton methods.
|
42
42
|
class << self
|
43
43
|
# Allows you setup Rubernate by yours implementaion of Configuration.
|
44
|
-
# See also method config
|
45
|
-
def configuration= config
|
44
|
+
# See also method config
|
45
|
+
def configuration= config # :nodoc:
|
46
46
|
Log.info "Rubernate configured by #{config}"
|
47
47
|
@factory = config
|
48
48
|
end
|
@@ -63,37 +63,14 @@ module Rubernate
|
|
63
63
|
self.configuration = DBI::Configuration.new impl, init, url, user, password
|
64
64
|
end
|
65
65
|
|
66
|
-
# Loads MySQL module
|
67
|
-
def require_mysql
|
68
|
-
require 'rubernate/impl/dbi_mysql'
|
69
|
-
require 'rubernate/init/init_mysql'
|
70
|
-
[DBI::MySqlRuntime, DBI::MySqlInit.new]
|
71
|
-
end
|
72
|
-
|
73
|
-
# Loads Oracle module
|
74
|
-
def require_oracle
|
75
|
-
require 'rubernate/impl/dbi_oracle'
|
76
|
-
require 'rubernate/init/init_oracle'
|
77
|
-
[DBI::OracleRuntime, DBI::OracleInit.new]
|
78
|
-
end
|
79
|
-
|
80
|
-
# Loads Postgres module
|
81
|
-
def require_pg
|
82
|
-
require 'rubernate/impl/dbi_pg'
|
83
|
-
require 'rubernate/init/init_pg'
|
84
|
-
[DBI::PgRuntime, DBI::PgInit.new]
|
85
|
-
end
|
86
|
-
|
87
|
-
# Loads Memory module used only for testing
|
88
|
-
def require_memory
|
89
|
-
require 'rubernate/impl/memory'
|
90
|
-
Memory::Runtime
|
91
|
-
end
|
92
66
|
|
93
67
|
# Intializes database - creates necessary tables, sequences and indices.
|
94
68
|
def init_db
|
95
69
|
@factory.init_db
|
96
70
|
end
|
71
|
+
|
72
|
+
# Global scope settings
|
73
|
+
attr_reader :settings
|
97
74
|
|
98
75
|
# Returns Runtime object associated with current session
|
99
76
|
# or nil if session does not excists
|
@@ -108,8 +85,9 @@ module Rubernate
|
|
108
85
|
Thread.current[:Rubernate] != nil
|
109
86
|
end
|
110
87
|
|
111
|
-
# Begins new
|
88
|
+
# Begins new session and transaction in database. Returns Runtime object or supply it to block if one is given.
|
112
89
|
def session
|
90
|
+
raise 'No config found. Call Rubernate.config first.' unless @factory
|
113
91
|
runtime = @factory.create
|
114
92
|
Thread.current[:Rubernate] = runtime
|
115
93
|
runtime.begin
|
@@ -125,19 +103,59 @@ module Rubernate
|
|
125
103
|
end
|
126
104
|
result
|
127
105
|
end
|
128
|
-
|
106
|
+
private
|
107
|
+
# Loads MySQL module
|
108
|
+
def require_mysql # :nodoc:
|
109
|
+
require 'rubernate/impl/dbi_mysql'
|
110
|
+
require 'rubernate/init/init_mysql'
|
111
|
+
[DBI::MySqlRuntime, DBI::MySqlInit.new]
|
112
|
+
end
|
129
113
|
|
130
|
-
|
114
|
+
# Loads Oracle module
|
115
|
+
def require_oracle # :nodoc:
|
116
|
+
require 'rubernate/impl/dbi_oracle'
|
117
|
+
require 'rubernate/init/init_oracle'
|
118
|
+
[DBI::OracleRuntime, DBI::OracleInit.new]
|
119
|
+
end
|
120
|
+
|
121
|
+
# Loads Postgres module
|
122
|
+
def require_pg # :nodoc:
|
123
|
+
require 'rubernate/impl/dbi_pg'
|
124
|
+
require 'rubernate/init/init_pg'
|
125
|
+
[DBI::PgRuntime, DBI::PgInit.new]
|
126
|
+
end
|
127
|
+
|
128
|
+
# Loads Memory module used only for testing :nodoc:
|
129
|
+
def require_memory # :nodoc:
|
130
|
+
require 'rubernate/impl/memory'
|
131
|
+
Memory::Runtime
|
132
|
+
end
|
133
|
+
end
|
134
|
+
@settings = {}
|
135
|
+
|
136
|
+
# Finds object by primary key. This method is actually shortcut to Runtime.find_by_pk
|
131
137
|
def find_by_pk pk
|
132
138
|
Rubernate.runtime.find_by_pk pk
|
133
139
|
end
|
134
140
|
|
135
|
-
#
|
141
|
+
# Finds object by query. This method is actually shortcut to Runtime.find_by_query
|
142
|
+
# Retruns array of objects, accepts native sql or query language expressions.
|
143
|
+
# If query is sql then +params+ should be array as in DBI. Else it should be Hash of <tt>{:maker=>value}</tt>.
|
144
|
+
#
|
145
|
+
# :call-seq: find_by_query(query, params={})
|
146
|
+
#
|
147
|
+
# Rubernate.find_by_query 'Select :p; Where p.name.str == :name', :name => 'Andy'
|
148
|
+
#
|
149
|
+
# #Or the same using native sql.
|
150
|
+
# Rubernate.find_by_query "select p_.* from r_objects p_ left outer join r_params p_name
|
151
|
+
# on (p_.object_pk = p_name.object_pk)
|
152
|
+
# where p_name.name = 'name' and p_name.str_value = ?", ['Andy']
|
153
|
+
#
|
136
154
|
def find_by_query query, params={}
|
137
155
|
Rubernate.runtime.find_by_query query, params
|
138
156
|
end
|
139
157
|
|
140
|
-
#
|
158
|
+
# Returns current runtime object.
|
141
159
|
def runtime
|
142
160
|
Rubernate.runtime
|
143
161
|
end
|
data/tests/config.rb
CHANGED
@@ -1,15 +1,17 @@
|
|
1
1
|
# Set to true to run tests on Oracle
|
2
2
|
$run_oracle_tests = false
|
3
3
|
|
4
|
-
|
5
|
-
|
4
|
+
module Rubernate
|
5
|
+
|
6
|
+
ORA_RUNTIME_CLASS = require_oracle[0]
|
7
|
+
ORA_DB_URL = 'dbi:OCI8:nc65db'
|
6
8
|
ORA_DB_USER = 'netcracker65'
|
7
9
|
ORA_DB_PWD = 'netcracker65'
|
8
10
|
|
9
11
|
# Set to true to run tests on MySQL
|
10
12
|
$run_mysql_tests = false
|
11
13
|
|
12
|
-
MYSQL_RUNTIME_CLASS =
|
14
|
+
MYSQL_RUNTIME_CLASS = require_mysql[0]
|
13
15
|
MYSQL_DB_URL = 'dbi:Mysql:rubernate_db:localhost'
|
14
16
|
MYSQL_DB_USER = nil
|
15
17
|
MYSQL_DB_PWD = nil
|
@@ -17,12 +19,13 @@ MYSQL_DB_PWD = nil
|
|
17
19
|
# Set to true to run tests on PostgreSQL
|
18
20
|
$run_pg_tests = false
|
19
21
|
|
20
|
-
PG_RUNTIME_CLASS =
|
22
|
+
PG_RUNTIME_CLASS = require_pg[0]
|
21
23
|
PG_DB_URL = 'dbi:Pg:rubernate_db'
|
22
24
|
PG_DB_USER = 'rn_test'
|
23
25
|
PG_DB_PWD = 'rn_password'
|
24
26
|
|
25
|
-
|
27
|
+
require_memory
|
28
|
+
end
|
26
29
|
|
27
30
|
# Log4r configuration
|
28
31
|
Rubernate::Log.level = Log4r::DEBUG
|