og 0.15.0 → 0.16.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.
Files changed (46) hide show
  1. data/CHANGELOG +77 -0
  2. data/INSTALL +3 -0
  3. data/README +5 -3
  4. data/Rakefile +148 -5
  5. data/benchmark/bench.rb +1 -1
  6. data/doc/AUTHORS +4 -4
  7. data/doc/RELEASES +41 -1
  8. data/examples/mock_example.rb +1 -1
  9. data/examples/mysql_to_psql.rb +1 -1
  10. data/examples/run.rb +1 -1
  11. data/install.rb +1 -1
  12. data/lib/og.rb +4 -2
  13. data/lib/og/{adapter.rb → adapters/base.rb} +334 -152
  14. data/lib/og/adapters/filesys.rb +3 -7
  15. data/lib/og/adapters/mysql.rb +5 -9
  16. data/lib/og/adapters/oracle.rb +5 -9
  17. data/lib/og/adapters/psql.rb +5 -9
  18. data/lib/og/adapters/sqlite.rb +5 -9
  19. data/lib/og/adapters/sqlserver.rb +5 -9
  20. data/lib/og/database.rb +13 -11
  21. data/lib/og/enchant.rb +1 -1
  22. data/lib/og/errors.rb +21 -0
  23. data/lib/og/meta.rb +10 -9
  24. data/lib/og/mixins/hierarchical.rb +4 -4
  25. data/lib/og/mixins/orderable.rb +1 -1
  26. data/lib/og/mixins/timestamped.rb +24 -0
  27. data/lib/og/mixins/tree.rb +3 -1
  28. data/lib/og/testing/mock.rb +2 -2
  29. data/lib/og/typemacros.rb +1 -1
  30. data/lib/og/validation.rb +4 -4
  31. data/test/og/{tc_filesys.rb → adapters/tc_filesys.rb} +1 -1
  32. data/test/og/{tc_sqlite.rb → adapters/tc_sqlite.rb} +3 -4
  33. data/test/og/{tc_sqlserver.rb → adapters/tc_sqlserver.rb} +5 -2
  34. data/test/og/mixins/tc_hierarchical.rb +0 -1
  35. data/test/og/mixins/tc_orderable.rb +0 -1
  36. data/test/og/tc_automanage.rb +3 -2
  37. data/test/og/tc_lifecycle.rb +15 -14
  38. data/test/og/tc_many_to_many.rb +0 -1
  39. data/test/og/tc_meta.rb +5 -5
  40. data/test/og/tc_validation.rb +1 -1
  41. data/test/tc_og.rb +0 -20
  42. metadata +33 -34
  43. data/examples/test.db +0 -0
  44. data/lib/og/connection.rb +0 -325
  45. data/lib/og/observer.rb +0 -53
  46. data/test/og/tc_observer.rb +0 -85
Binary file
@@ -1,325 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: connection.rb 326 2005-03-28 11:07:17Z gmosx $
4
-
5
- module Og;
6
-
7
- require 'glue/property'
8
- require 'glue/array'
9
- require 'glue/time'
10
-
11
- # A Connection to the Database. This file defines the skeleton
12
- # functionality. A backend specific implementation file (driver)
13
- # implements all methods.
14
- #
15
- #--
16
- # - support caching.
17
- # - support prepared statements.
18
- #++
19
-
20
- class Connection
21
-
22
- # The Og database object.
23
-
24
- attr_reader :db
25
-
26
- # The actual connection to the backend store.
27
-
28
- attr_accessor :store
29
-
30
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
31
- # :section: Backend connection methods.
32
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
33
-
34
- # Initialize a connection to the database.
35
-
36
- def initialize(db)
37
- @db = db
38
- Logger.debug "Created DB connection." if $DBG
39
- end
40
-
41
- # Close the connection to the database.
42
-
43
- def close
44
- Logger.debug "Closed DB connection." if $DBG
45
- end
46
-
47
- # Create the managed object table. The properties of the
48
- # object are mapped to the table columns. Additional sql relations
49
- # and constrains are created (indicices, sequences, etc).
50
-
51
- def create_table(klass)
52
- raise 'Not implemented!'
53
- end
54
-
55
- # Drop the managed object table.
56
-
57
- def drop_table(klass)
58
- exec "DROP TABLE #{klass::DBTABLE}"
59
- end
60
-
61
- # Prepare an sql statement.
62
-
63
- def prepare(sql)
64
- raise 'Not implemented!'
65
- end
66
-
67
- # Execute an SQL query and return the result.
68
-
69
- def query(sql)
70
- raise 'Not implemented!'
71
- end
72
-
73
- # Execute an SQL query, no result returned.
74
-
75
- def exec(sql)
76
- raise 'Not implemented!'
77
- end
78
- alias_method :execute, :exec
79
-
80
- # Start a new transaction.
81
-
82
- def start
83
- exec 'START TRANSACTION'
84
- end
85
-
86
- # Commit a transaction.
87
-
88
- def commit
89
- exec 'COMMIT'
90
- end
91
-
92
- # Rollback a transaction.
93
-
94
- def rollback
95
- exec 'ROLLBACK'
96
- end
97
-
98
- # Transaction helper. In the transaction block use
99
- # the db pointer to the backend.
100
-
101
- def transaction(&block)
102
- begin
103
- start
104
- yield(self)
105
- commit
106
- rescue => ex
107
- Logger.error "DB Error: ERROR IN TRANSACTION"
108
- Logger.error "#{ex}"
109
- Logger.error "#{ex.backtrace}"
110
- rollback
111
- end
112
- end
113
-
114
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
115
- # :section: Deserialization methods.
116
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
117
-
118
- # Is the given resultset valid?
119
-
120
- def valid_res?(res)
121
- return !(res.nil?)
122
- end
123
-
124
- # Read (deserialize) one row of the resultset.
125
-
126
- def read_one(res, klass)
127
- raise 'Not implemented!'
128
- end
129
-
130
- # Read (deserialize) all rows of the resultset.
131
-
132
- def read_all(res, klass)
133
- raise 'Not implemented!'
134
- end
135
-
136
- # Read the first column of the resultset as an Integer.
137
-
138
- def read_int(res, idx = 0)
139
- raise 'Not implemented!'
140
- end
141
-
142
- # Get a row from the resultset.
143
-
144
- def get_row(res)
145
- return res
146
- end
147
-
148
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
149
- # :section: Managed object methods.
150
- # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
151
-
152
- # Save an object to the database. Insert if this is a new object or
153
- # update if this is already stored in the database.
154
-
155
- def save(obj)
156
- if obj.oid
157
- # object allready inserted, update!
158
- obj.og_update(self)
159
- else
160
- # not in the database, insert!
161
- obj.og_insert(self)
162
- end
163
- end
164
- alias_method :<<, :save
165
- alias_method :put, :save
166
-
167
- # Force insertion of managed object.
168
-
169
- def insert(obj)
170
- obj.og_insert(self)
171
- end
172
-
173
- # Force update of managed object.
174
-
175
- def update(obj)
176
- obj.og_update(self)
177
- end
178
-
179
- # Update only specific fields of the managed object.
180
- #
181
- # Input:
182
- # sql = the sql code to updated the properties.
183
- #
184
- # WARNING: the object in memory is not updated.
185
- #--
186
- # TODO: should update the object in memory.
187
- #++
188
-
189
- def update_properties(update_sql, obj_or_oid, klass = nil)
190
- oid = obj_or_oid.to_i
191
- klass = obj_or_oid.class unless klass
192
-
193
- exec "UPDATE #{klass::DBTABLE} SET #{update_sql} WHERE oid=#{oid}"
194
- end
195
- alias_method :pupdate, :update_properties
196
- alias_method :update_propery, :update_properties
197
-
198
- # Load an object from the database.
199
- #
200
- # Input:
201
- # oid = the object oid, OR the object name.
202
-
203
- def load(oid, klass)
204
- if oid.to_i > 0 # a valid Fixnum ?
205
- load_by_oid(oid, klass)
206
- else
207
- load_by_name(oid, klass)
208
- end
209
- end
210
- alias_method :get, :load
211
-
212
- # Load an object by oid.
213
-
214
- def load_by_oid(oid, klass)
215
- res = query "SELECT * FROM #{klass::DBTABLE} WHERE oid=#{oid}"
216
- read_one(res, klass)
217
- end
218
- alias_method :get_by_oid, :load_by_oid
219
-
220
- # Load an object by name.
221
-
222
- def load_by_name(name, klass)
223
- res = query "SELECT * FROM #{klass::DBTABLE} WHERE name='#{name}'"
224
- read_one(res, klass)
225
- end
226
- alias_method :get_by_name, :load_by_name
227
-
228
- # Load all objects of the given klass.
229
- # Used to be called 'collect' in an earlier version.
230
-
231
- def load_all(klass, extrasql = nil)
232
- res = query "SELECT * FROM #{klass::DBTABLE} #{extrasql}"
233
- read_all(res, klass)
234
- end
235
- alias_method :get_all, :load_all
236
-
237
- # Perform a standard SQL query to the database. Deserializes the
238
- # results.
239
-
240
- def select(sql, klass)
241
- unless sql =~ /SELECT/i
242
- sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
243
- end
244
-
245
- res = query(sql)
246
- read_all(res, klass)
247
- end
248
-
249
- # Optimized for one result.
250
-
251
- def select_one(sql, klass)
252
- unless sql =~ /SELECT/i
253
- sql = "SELECT * FROM #{klass::DBTABLE} WHERE #{sql}"
254
- end
255
-
256
- res = query(sql)
257
- read_one(res, klass)
258
- end
259
-
260
- # Perform a count query.
261
-
262
- def count(sql, klass = nil)
263
- unless sql =~ /SELECT/i
264
- sql = "SELECT COUNT(*) FROM #{klass::DBTABLE} WHERE #{sql}"
265
- end
266
-
267
- res = query(sql)
268
- return read_int(res)
269
- end
270
-
271
- # Delete an object from the database. Allways perform a deep delete.
272
- #
273
- # No need to optimize here with pregenerated code. Deletes are
274
- # not used as much as reads or writes.
275
- #
276
- # Input:
277
- #
278
- # obj_or_oid = Object or oid to delete.
279
- # klass = Class of object (can be nil if an object is passed)
280
- #
281
- #--
282
- # TODO: pre evaluate for symmetry to the other methods
283
- #++
284
-
285
- def delete(obj_or_oid, klass = nil, cascade = true)
286
- oid = obj_or_oid.to_i
287
- klass = obj_or_oid.class unless klass
288
-
289
- # this is a class callback!
290
-
291
- if klass.respond_to?(:og_pre_delete)
292
- klass.og_pre_delete(self, obj_or_oid)
293
- end
294
-
295
- # TODO: implement this as stored procedure? naaah.
296
- # TODO: also handle many_to_many relations.
297
-
298
- transaction do |tx|
299
- tx.exec "DELETE FROM #{klass::DBTABLE} WHERE oid=#{oid}"
300
- if cascade and klass.__meta.include?(:descendants)
301
- klass.__meta[:descendants].each do |dclass, linkback|
302
- tx.exec "DELETE FROM #{dclass::DBTABLE} WHERE #{linkback}=#{oid}"
303
- end
304
- end
305
- end
306
- end
307
- alias_method :delete!, :delete
308
-
309
- protected
310
-
311
- # Handles an adapter exception.
312
-
313
- def handle_db_exception(ex, sql = nil)
314
- Logger.error "DB error #{ex}, [#{sql}]"
315
- Logger.error ex.backtrace.join("\n")
316
- raise SqlException.new(ex, sql) if Og.raise_db_exceptions
317
-
318
- # FIXME: should return :error or something.
319
-
320
- return nil
321
- end
322
-
323
- end
324
-
325
- end
@@ -1,53 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: observer.rb 281 2005-03-10 12:24:14Z gmosx $
4
-
5
- module Og
6
-
7
- # Classes that include this module can be tracked by
8
- # Observers. The observer mechanism utilizes duck typing
9
- # so you can attach any class that responds to the
10
- # Og lifycycle callback methods. However, classes extended
11
- # from the Observer base class are typically used.
12
-
13
- module Observable
14
- def add_observer(*observer)
15
- for o in observer.flatten
16
- meta :og_observers, o
17
- self.og_db.adapter.eval_lifecycle_methods(self, self.og_db)
18
- end
19
- end
20
- end
21
-
22
- # Observers are attached to managed classes to track their
23
- # Lifecycle. This way, the 'polution' of the original class
24
- # with excess responsibility is avoided.
25
- #
26
- # An observer can implement the standard Og lifecycle
27
- # callbacks:
28
- #
29
- # * og_pre_read
30
- # * og_post_read
31
- # * og_pre_insert
32
- # * og_post_insert
33
- # * og_pre_update
34
- # * og_post_update
35
- # * og_pre_insert_update
36
- # * og_post_insert_update
37
- # * self.og_pre_delete
38
- #
39
- # Based on code from ActiveRecord (http://www.rubyonrails.com)
40
-
41
- class Observer
42
- include Singleton
43
-
44
- # Attaches the observer to the supplied classes.
45
-
46
- def self.observe(*classes)
47
- for c in classes.flatten
48
- c.meta :og_observers, self
49
- end
50
- end
51
- end
52
-
53
- end
@@ -1,85 +0,0 @@
1
- $:.unshift File.join(File.dirname(__FILE__), '..', '..', 'lib')
2
-
3
- require 'test/unit'
4
- require 'ostruct'
5
-
6
- require 'og'
7
-
8
- class TC_OgObserver < Test::Unit::TestCase # :nodoc: all
9
- include N
10
-
11
- class User
12
- property :name
13
- end
14
-
15
- class Article
16
- property :name, String
17
- property :age, Fixnum
18
-
19
- def initialize (name = nil, age = nil)
20
- @name, @age = name, age
21
- end
22
- end
23
-
24
- # Example of a simple class that acts as an observer.
25
-
26
- class ArticleObserver
27
- attr :count
28
-
29
- def initialize
30
- @count = 0
31
- end
32
-
33
- def og_pre_insert(conn, obj)
34
- @count += 1
35
- end
36
- end
37
-
38
- # Rails style observer, baaah...
39
-
40
- class MultiObserver < Og::Observer
41
- observe Article, User
42
-
43
- attr :count
44
-
45
- def initialize
46
- @count = 0
47
- end
48
-
49
- def og_pre_insert(conn, obj)
50
- @count += 1
51
- end
52
- end
53
-
54
- def setup
55
- end
56
-
57
- def teardown
58
- end
59
-
60
- def test_all
61
- config = {
62
- :adapter => 'psql',
63
- :database => 'test',
64
- :user => 'postgres',
65
- :password => 'navelrulez',
66
- :connection_count => 2
67
- }
68
-
69
- Og::Database.drop_db!(config)
70
- og = Og::Database.new(config)
71
-
72
- og.get_connection
73
-
74
- ao = ArticleObserver.new
75
- Article.add_observer(ao)
76
- Article.create('Hello', 5)
77
-
78
- assert_equal 1, ao.count
79
- assert_equal 1, MultiObserver.instance.count
80
-
81
- og.put_connection
82
- og.shutdown
83
- end
84
-
85
- end