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/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
+
@@ -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