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
data/lib/rubernate.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#--
|
2
|
+
# Rubernate is an object-oriented storage for Ruby objects based
|
3
|
+
# on relational database model.
|
4
|
+
#
|
5
|
+
# Copyright (C) 2006 Andrey Ryabov <andrey_ryabov@bk.ru>
|
6
|
+
#
|
7
|
+
# This program is free software; you can redistribute it and/or modify
|
8
|
+
# it under the terms of the GNU General Public License as published by
|
9
|
+
# the Free Software Foundation; either version 2 of the License, or
|
10
|
+
# (at your option) any later version.
|
11
|
+
#
|
12
|
+
# This program is distributed in the hope that it will be useful,
|
13
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
14
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
15
|
+
# GNU General Public License for more details.
|
16
|
+
#
|
17
|
+
# You should have received a copy of the GNU General Public License
|
18
|
+
# along with this program; if not, write to the Free Software
|
19
|
+
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
20
|
+
#++
|
21
|
+
|
22
|
+
$:.unshift File.dirname(__FILE__) unless
|
23
|
+
$:.include? File.dirname(__FILE__)
|
24
|
+
|
25
|
+
require 'log4r'
|
26
|
+
require 'rubernate/mixins'
|
27
|
+
require 'rubernate/callbacks'
|
28
|
+
require 'rubernate/entity'
|
29
|
+
require 'rubernate/peer'
|
30
|
+
require 'rubernate/runtime'
|
31
|
+
require 'rubernate/impl/dbigeneric'
|
32
|
+
require 'rubernate/queries'
|
33
|
+
require 'rubernate/impl/memory'
|
34
|
+
require 'rubernate/impl/dbimysql'
|
35
|
+
require 'rubernate/impl/dbioracle'
|
36
|
+
|
37
|
+
|
38
|
+
module Rubernate
|
39
|
+
# Common Rubernate Log instance
|
40
|
+
Log = Log4r::Logger.new self.name
|
41
|
+
|
42
|
+
# Rubernate core singelton methods.
|
43
|
+
class << self
|
44
|
+
# Allows you setup Rubernate by yours implementaion of RuntimeFactory.
|
45
|
+
# See also method configure
|
46
|
+
def runtime_factory= factory
|
47
|
+
Log.info "Rubernate configured by #{factory}"
|
48
|
+
@runtime_factory = factory
|
49
|
+
end
|
50
|
+
|
51
|
+
# Performs configuration of Rubernate.
|
52
|
+
# * db - database type (:mysql, :oracle).
|
53
|
+
# * url - url of database.
|
54
|
+
# * user - databases user name
|
55
|
+
# * password - databases users password
|
56
|
+
def config db, url, user, password
|
57
|
+
impl = case db
|
58
|
+
when :mysql: DBI::MySqlRuntime
|
59
|
+
when :oracle: DBI::OracleRuntime
|
60
|
+
else raise "Database #{db} not supported"
|
61
|
+
end
|
62
|
+
self.runtime_factory = DBI::RuntimeFactory.new impl, url, user, password
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns Runtime object associated with current session
|
66
|
+
# or nil if session does not excists
|
67
|
+
def runtime
|
68
|
+
Thread.current[:Rubernate]
|
69
|
+
end
|
70
|
+
|
71
|
+
# Checks if session exists
|
72
|
+
def session?
|
73
|
+
Thread.current[:Rubernate] != nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# Begins new Rubernate session
|
77
|
+
def session
|
78
|
+
runtime = @runtime_factory.create
|
79
|
+
Thread.current[:Rubernate] = runtime
|
80
|
+
runtime.begin
|
81
|
+
return runtime unless block_given?
|
82
|
+
begin
|
83
|
+
result = yield runtime
|
84
|
+
runtime.commit
|
85
|
+
rescue Exception => e
|
86
|
+
runtime.rollback e
|
87
|
+
raise
|
88
|
+
ensure
|
89
|
+
Thread.current[:Rubernate] = nil
|
90
|
+
end
|
91
|
+
result
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Shortcut method can be used instead of Rubernate.runtime.find_by_pk
|
96
|
+
def find_by_pk pk
|
97
|
+
Rubernate.runtime.find_by_pk pk
|
98
|
+
end
|
99
|
+
|
100
|
+
# Shortcut method can be used instead of Rubernate.runtime.find_by_query
|
101
|
+
def find_by_query query, params={}
|
102
|
+
Rubernate.runtime.find_by_query query, params
|
103
|
+
end
|
104
|
+
|
105
|
+
# Shortcut method can be used instead of Rubernate.runtime
|
106
|
+
def runtime
|
107
|
+
Rubernate.runtime
|
108
|
+
end
|
109
|
+
|
110
|
+
# Shortcut method can be used instead of Rubernate.runtime.dbh
|
111
|
+
# Retruns DBI::DatabaseHandle bound to current transaction.
|
112
|
+
# This method is optional, some implementation may not implement this method.
|
113
|
+
def dbh
|
114
|
+
Rubernate.runtime.dbh
|
115
|
+
end
|
116
|
+
module_function :find_by_pk, :find_by_query, :dbh
|
117
|
+
|
118
|
+
# Signals that object wasn't found
|
119
|
+
class ObjectNotFoundException < RuntimeError
|
120
|
+
def initialize msg
|
121
|
+
@msg = msg.is_a?(String) ? msg : "object not found: #{msg}"
|
122
|
+
end
|
123
|
+
def to_s
|
124
|
+
@msg
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/tests/README
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
== Configuration
|
2
|
+
|
3
|
+
Before you start testing edit config.rb to setup
|
4
|
+
appropriate database configuration. It is encouraged
|
5
|
+
to use one database dedicated to tests since all its
|
6
|
+
content will be deleted during the tests.
|
7
|
+
|
8
|
+
== Running
|
9
|
+
You can run all tests by:
|
10
|
+
|
11
|
+
test>ruby all_tests.rb
|
12
|
+
|
13
|
+
Or you can run each test by hand:
|
14
|
+
|
15
|
+
test\rubernate>ruby queries_test.rb
|
16
|
+
|
data/tests/all_tests.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
#
|
2
|
+
# Edit to setup database connection
|
3
|
+
#
|
4
|
+
require 'rubernate/fixtures'
|
5
|
+
require 'config'
|
6
|
+
require 'rubernate/rubernate_test'
|
7
|
+
require 'rubernate/utils_test'
|
8
|
+
require 'rubernate/queries_test'
|
9
|
+
require 'rubernate/impl/memory_test'
|
10
|
+
require 'rubernate/callbacks_test'
|
11
|
+
require 'rubernate/impl/dbioracle_test' if $run_oracle_tests
|
12
|
+
require 'rubernate/impl/dbimysql_test' if $run_mysql_tests
|
data/tests/config.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# Set to true to run tests or Oracle
|
2
|
+
$run_oracle_tests = true
|
3
|
+
|
4
|
+
ORA_RUNTIME_CLASS = Rubernate::DBI::OracleRuntime
|
5
|
+
ORA_DB_URL = 'dbi:OCI8:dbg91'
|
6
|
+
ORA_DB_USER = 'netcracker65'
|
7
|
+
ORA_DB_PWD = 'netcracker65'
|
8
|
+
|
9
|
+
# Set to true to run tests on MySQL
|
10
|
+
$run_mysql_tests = true
|
11
|
+
|
12
|
+
MYSQL_RUNTIME_CLASS = Rubernate::DBI::MySqlRuntime
|
13
|
+
MYSQL_DB_URL = 'dbi:Mysql:rubernate_db:localhost'
|
14
|
+
MYSQL_DB_USER = nil
|
15
|
+
MYSQL_DB_PWD = nil
|
16
|
+
|
17
|
+
# Log4r configuration
|
18
|
+
$level = Log4r::DEBUG
|
19
|
+
$outputter = Log4r::FileOutputter.new 'file',
|
20
|
+
:filename => File.dirname(__FILE__) + "/../logs/#{$0}.log",
|
21
|
+
:trunc => true
|
22
|
+
|
23
|
+
# TODO: make external log configuration
|
24
|
+
# Update level for all logs needed
|
25
|
+
module Rubernate
|
26
|
+
Log.add $outputter
|
27
|
+
Log.level = $level
|
28
|
+
class Runtime
|
29
|
+
Log.level = $level
|
30
|
+
end
|
31
|
+
module Queries
|
32
|
+
Log.level = $level
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
$:.unshift(File.expand_path('../..', __FILE__)) unless
|
2
|
+
$:.include?(File.expand_path('../..', __FILE__))
|
3
|
+
|
4
|
+
require 'rubernate/fixtures'
|
5
|
+
|
6
|
+
module Rubernate
|
7
|
+
module Callbacks
|
8
|
+
Event = Struct.new :object, :property, :old_value, :new_value
|
9
|
+
class << self
|
10
|
+
attr_accessor :handlers, :events
|
11
|
+
def clear
|
12
|
+
self.handlers, self.events = [], []
|
13
|
+
end
|
14
|
+
end
|
15
|
+
clear
|
16
|
+
|
17
|
+
# This module contains callback methods and included in module Rubernate::Entity
|
18
|
+
module Entity
|
19
|
+
def on_create
|
20
|
+
Callbacks.handlers << :on_create
|
21
|
+
Callbacks.events << Event.new(self)
|
22
|
+
end
|
23
|
+
|
24
|
+
def on_remove
|
25
|
+
Callbacks.handlers << :on_remove
|
26
|
+
Callbacks.events << Event.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def on_save
|
30
|
+
Callbacks.handlers << :on_save
|
31
|
+
Callbacks.events << Event.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def on_load
|
35
|
+
Callbacks.handlers << :on_load
|
36
|
+
Callbacks.events << Event.new(self)
|
37
|
+
end
|
38
|
+
|
39
|
+
def on_change property, old_value, new_value
|
40
|
+
Callbacks.handlers << :on_change
|
41
|
+
Callbacks.events << Event.new(self, property, old_value, new_value)
|
42
|
+
end
|
43
|
+
|
44
|
+
def on_modify property, old_value, new_value
|
45
|
+
Callbacks.handlers << :on_modify
|
46
|
+
Callbacks.events << Event.new(self, property, old_value, new_value)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# This module contains callback method and included in module Rubernate::Runtime
|
51
|
+
module Runtime
|
52
|
+
def self.def_callback name
|
53
|
+
module_eval %{
|
54
|
+
def #{name} *params
|
55
|
+
Callbacks.handlers << :#{name}
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
%w{on_begin on_rollback
|
60
|
+
before_commit after_commit
|
61
|
+
before_flush after_flush
|
62
|
+
}.each {|name| def_callback name}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class CallbacksTest < Test::Unit::TestCase
|
67
|
+
include Rubernate
|
68
|
+
include FixtureClasses
|
69
|
+
|
70
|
+
def setup
|
71
|
+
@factory = Rubernate::Memory::RuntimeFactory.new
|
72
|
+
@runtime = @factory.create
|
73
|
+
Rubernate.runtime_factory = @factory
|
74
|
+
Callbacks.clear
|
75
|
+
end
|
76
|
+
|
77
|
+
def teardown
|
78
|
+
Callbacks.clear
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_create_callbacks
|
82
|
+
o = nil
|
83
|
+
Rubernate.session {o = C1.new.attach}
|
84
|
+
assert_equal [:on_begin, :on_create, :before_commit, :before_flush, :after_commit], Callbacks.handlers
|
85
|
+
assert_equal o, Callbacks.events[0].object
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_remove_callbacks
|
89
|
+
Rubernate.session {o = C3.new.attach; o.remove!}
|
90
|
+
assert_equal [:on_begin, :on_create, :on_remove, :before_commit, :before_flush, :after_commit], Callbacks.handlers
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_load_callbacks
|
94
|
+
pk = nil
|
95
|
+
Rubernate.session {o = C1.new.attach; pk = o.primary_key}
|
96
|
+
Callbacks.handlers.clear
|
97
|
+
Rubernate.session {o = find_by_pk pk}
|
98
|
+
assert_equal [:on_begin, :on_load, :before_commit, :before_flush, :after_commit], Callbacks.handlers
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_save_change_callback
|
102
|
+
Rubernate.session {o = C1.new.attach; o.p1 = 'v1'}
|
103
|
+
assert_equal [:on_begin, :on_create, :on_change,
|
104
|
+
:before_commit, :before_flush, :on_save, :after_flush, :after_commit], Callbacks.handlers
|
105
|
+
assert_equal :p1, Callbacks.events[1].property
|
106
|
+
assert_nil Callbacks.events[1].old_value
|
107
|
+
assert_equal 'v1', Callbacks.events[1].new_value
|
108
|
+
end
|
109
|
+
|
110
|
+
def test_modify_callbacks
|
111
|
+
pk = nil
|
112
|
+
Rubernate.session {o = C1.new.attach; o.p1 = [o]; pk = o.primary_key}
|
113
|
+
Callbacks.clear
|
114
|
+
Rubernate.session {o = find_by_pk pk; o.p1 << o}
|
115
|
+
assert_equal [:on_begin, :on_load, :before_commit,
|
116
|
+
:on_modify, :on_modify, :before_flush, :on_save,
|
117
|
+
:after_flush, :after_commit], Callbacks.handlers
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
$:.unshift(File.expand_path('../..', __FILE__)) unless
|
2
|
+
$:.include?(File.expand_path('../..', __FILE__))
|
3
|
+
$:.unshift File.dirname(__FILE__) + '/../../lib'
|
4
|
+
|
5
|
+
require 'rubernate'
|
6
|
+
require 'rubernate/impl/memory'
|
7
|
+
require 'test/unit'
|
8
|
+
|
9
|
+
module FixtureClasses
|
10
|
+
class C0; attr :p0, false end
|
11
|
+
class C1; persistent :p1; end
|
12
|
+
class C2 < C1; persistent :p2; end
|
13
|
+
class C3 < C0;
|
14
|
+
persistent :p0
|
15
|
+
def on_load
|
16
|
+
Thread.current[:on_load] = self
|
17
|
+
end
|
18
|
+
def on_save
|
19
|
+
Thread.current[:on_save] = self
|
20
|
+
end
|
21
|
+
end
|
22
|
+
class C4 < C2
|
23
|
+
def initialize pk = nil
|
24
|
+
@primary_key = pk
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|