og 0.16.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. data/CHANGELOG +485 -0
  2. data/README +35 -12
  3. data/Rakefile +4 -7
  4. data/benchmark/bench.rb +1 -1
  5. data/doc/AUTHORS +3 -3
  6. data/doc/RELEASES +153 -2
  7. data/doc/config.txt +0 -7
  8. data/doc/tutorial.txt +7 -0
  9. data/examples/README +5 -0
  10. data/examples/mysql_to_psql.rb +25 -50
  11. data/examples/run.rb +62 -77
  12. data/install.rb +1 -1
  13. data/lib/og.rb +45 -106
  14. data/lib/og/collection.rb +156 -0
  15. data/lib/og/entity.rb +131 -0
  16. data/lib/og/errors.rb +10 -15
  17. data/lib/og/manager.rb +115 -0
  18. data/lib/og/{mixins → mixin}/hierarchical.rb +43 -37
  19. data/lib/og/{mixins → mixin}/orderable.rb +35 -35
  20. data/lib/og/{mixins → mixin}/timestamped.rb +0 -6
  21. data/lib/og/{mixins → mixin}/tree.rb +0 -4
  22. data/lib/og/relation.rb +178 -0
  23. data/lib/og/relation/belongs_to.rb +14 -0
  24. data/lib/og/relation/has_many.rb +62 -0
  25. data/lib/og/relation/has_one.rb +17 -0
  26. data/lib/og/relation/joins_many.rb +69 -0
  27. data/lib/og/relation/many_to_many.rb +17 -0
  28. data/lib/og/relation/refers_to.rb +31 -0
  29. data/lib/og/store.rb +223 -0
  30. data/lib/og/store/filesys.rb +113 -0
  31. data/lib/og/store/madeleine.rb +4 -0
  32. data/lib/og/store/memory.rb +291 -0
  33. data/lib/og/store/mysql.rb +283 -0
  34. data/lib/og/store/psql.rb +238 -0
  35. data/lib/og/store/sql.rb +599 -0
  36. data/lib/og/store/sqlite.rb +190 -0
  37. data/lib/og/store/sqlserver.rb +262 -0
  38. data/lib/og/types.rb +19 -0
  39. data/lib/og/validation.rb +0 -4
  40. data/test/og/{mixins → mixin}/tc_hierarchical.rb +21 -23
  41. data/test/og/{mixins → mixin}/tc_orderable.rb +15 -14
  42. data/test/og/mixin/tc_timestamped.rb +38 -0
  43. data/test/og/store/tc_filesys.rb +71 -0
  44. data/test/og/tc_relation.rb +36 -0
  45. data/test/og/tc_store.rb +290 -0
  46. data/test/og/tc_types.rb +21 -0
  47. metadata +54 -40
  48. data/examples/mock_example.rb +0 -50
  49. data/lib/og/adapters/base.rb +0 -706
  50. data/lib/og/adapters/filesys.rb +0 -117
  51. data/lib/og/adapters/mysql.rb +0 -350
  52. data/lib/og/adapters/oracle.rb +0 -368
  53. data/lib/og/adapters/psql.rb +0 -272
  54. data/lib/og/adapters/sqlite.rb +0 -265
  55. data/lib/og/adapters/sqlserver.rb +0 -356
  56. data/lib/og/database.rb +0 -290
  57. data/lib/og/enchant.rb +0 -149
  58. data/lib/og/meta.rb +0 -407
  59. data/lib/og/testing/mock.rb +0 -165
  60. data/lib/og/typemacros.rb +0 -24
  61. data/test/og/adapters/tc_filesys.rb +0 -83
  62. data/test/og/adapters/tc_sqlite.rb +0 -86
  63. data/test/og/adapters/tc_sqlserver.rb +0 -96
  64. data/test/og/tc_automanage.rb +0 -46
  65. data/test/og/tc_lifecycle.rb +0 -105
  66. data/test/og/tc_many_to_many.rb +0 -61
  67. data/test/og/tc_meta.rb +0 -55
  68. data/test/og/tc_validation.rb +0 -89
  69. data/test/tc_og.rb +0 -364
@@ -1,265 +0,0 @@
1
- # * George Moschovitis <gm@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: sqlite.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- begin
6
- require 'sqlite3'
7
- rescue Object => ex
8
- Logger.error 'Ruby-Sqlite3 bindings are not installed!'
9
- Logger.error ex
10
- end
11
-
12
- require 'fileutils'
13
-
14
- require 'og/adapters/base'
15
-
16
- module Og
17
-
18
- # The SQLite adapter. This adapter communicates with
19
- # an SQLite3 rdbms. For extra documentation see
20
- # lib/og/adapter.rb
21
-
22
- class SqliteAdapter < Adapter
23
-
24
- def drop_db(database, user = nil, password = nil)
25
- begin
26
- FileUtils.rm("#{database}.db")
27
- super
28
- rescue
29
- Logger.error "Cannot drop '#{database}'!"
30
- end
31
- end
32
- =begin
33
- def write_prop(p)
34
- if p.klass.ancestors.include?(Integer)
35
- return "@#{p.symbol}"
36
- elsif p.klass.ancestors.include?(Float)
37
- return "@#{p.symbol}"
38
- elsif p.klass.ancestors.include?(String)
39
- return "#{self.class}.escape(@#{p.symbol})"
40
- elsif p.klass.ancestors.include?(Time)
41
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.timestamp(@#{p.symbol})\}'" : 'NULL'\}|
42
- elsif p.klass.ancestors.include?(Date)
43
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.date(@#{p.symbol})\}'" : 'NULL'\}|
44
- elsif p.klass.ancestors.include?(TrueClass)
45
- return "#\{@#{p.symbol} ? \"'t'\" : 'NULL' \}"
46
- else
47
- return %|#\{@#{p.symbol} ? "'#\{#{self.class}.escape(@#{p.symbol}.to_yaml)\}'" : "''"\}|
48
- end
49
- end
50
- =end
51
- def insert_code(klass, db)
52
- props = props_for_insert(klass)
53
- values = props.collect { |p| write_prop(p) }.join(',')
54
-
55
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values})"
56
-
57
- %{
58
- conn.store.query("#{sql}").close
59
- @oid = conn.store.last_insert_row_id
60
- }
61
- =begin
62
- props = props_for_insert(klass)
63
-
64
- placeholders = Array.new(props.size, '?').join(',')
65
- values = props.collect { |p| write_prop(p) }.join(',')
66
-
67
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{placeholders})"
68
- klass.class_eval %{
69
- cattr_accessor :og_insert_statement
70
- }
71
-
72
- klass.og_insert_statement = db.prepare(sql)
73
-
74
- %{
75
- #{pre_cb}
76
- @@og_insert_statement.execute(#{values})
77
- @oid = conn.store.last_insert_row_id
78
- #{post_cb}
79
- }
80
- =end
81
- end
82
-
83
- def new_connection(db)
84
- return SqliteConnection.new(db)
85
- end
86
-
87
- def calc_field_index(klass, db)
88
- res = db.query "SELECT * FROM #{klass::DBTABLE} LIMIT 1"
89
- meta = db.managed_classes[klass]
90
-
91
- columns = res.columns
92
-
93
- for idx in (0...columns.size)
94
- meta.field_index[columns[idx]] = idx
95
- end
96
-
97
- ensure
98
- res.close
99
- end
100
-
101
- def create_table(klass, db)
102
- conn = db.get_connection
103
-
104
- fields = create_fields(klass)
105
-
106
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
107
-
108
- # Create table constrains
109
-
110
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
111
- sql << ", #{constrains.join(', ')}"
112
- end
113
-
114
- sql << ");"
115
-
116
- # Create indices
117
-
118
- if klass.__meta and indices = klass.__meta[:sql_index]
119
- for data in indices
120
- idx, options = *data
121
- idx = idx.to_s
122
- pre_sql, post_sql = options[:pre], options[:post]
123
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
124
- sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
125
- end
126
- end
127
-
128
- begin
129
- conn.store.query(sql).close
130
- Logger.info "Created table '#{klass::DBTABLE}'."
131
- rescue Exception => ex
132
- # gmosx: any idea how to better test this?
133
- if ex.to_s =~ /table .* already exists/i
134
- Logger.debug "Table already exists" if $DBG
135
- return
136
- else
137
- raise
138
- end
139
- end
140
-
141
- # Create join tables if needed. Join tables are used in
142
- # 'many_to_many' relations.
143
-
144
- if klass.__meta and joins = klass.__meta[:sql_join]
145
- for data in joins
146
- # the class to join to and some options.
147
- join_name, join_class, options = *data
148
-
149
- # gmosx: dont use DBTABLE here, perhaps the join class
150
- # is not managed yet.
151
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
152
- join_src = "#{self.class.encode(klass)}_oid"
153
- join_dst = "#{self.class.encode(join_class)}_oid"
154
- begin
155
- conn.store.query("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").close
156
- conn.store.query("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").close
157
- conn.store.query("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").close
158
- rescue Exception => ex
159
- # gmosx: any idea how to better test this?
160
- if ex.to_s =~ /table .* already exists/i
161
- Logger.debug "Join table already exists" if $DBG
162
- else
163
- raise
164
- end
165
- end
166
- end
167
- end
168
-
169
- ensure
170
- db.put_connection
171
- end
172
-
173
- end
174
-
175
- # The SQLite connection.
176
-
177
- class SqliteConnection < Connection
178
-
179
- def initialize(db)
180
- @store = SQLite3::Database.new("#{db.config[:database]}.db")
181
- super
182
- end
183
-
184
- def close
185
- @store.close
186
- super
187
- end
188
-
189
- def prepare(sql)
190
- @store.prepare(sql)
191
- end
192
-
193
- def query(sql)
194
- Logger.debug sql if $DBG
195
- begin
196
- return @store.query(sql)
197
- rescue => ex
198
- handle_db_exception(ex, sql)
199
- end
200
- end
201
-
202
- def exec(sql)
203
- Logger.debug sql if $DBG
204
- begin
205
- @store.query(sql).close
206
- rescue => ex
207
- handle_db_exception(ex, sql)
208
- end
209
- end
210
-
211
- def start
212
- @store.transaction
213
- end
214
-
215
- def commit
216
- @store.commit
217
- end
218
-
219
- def rollback
220
- @store.rollback
221
- end
222
-
223
- def valid_res?(res)
224
- return !(res.nil?)
225
- end
226
-
227
- def read_one(res, klass)
228
- return nil unless valid_res?(res)
229
- row = res.next
230
- return nil unless row
231
-
232
- obj = klass.allocate
233
- obj.og_read(row)
234
-
235
- res.close
236
- return obj
237
- end
238
-
239
- def read_all(res, klass)
240
- return [] unless valid_res?(res)
241
- objects = []
242
-
243
- res.each do |row|
244
- obj = klass.allocate
245
- obj.og_read(row)
246
- objects << obj
247
- end
248
-
249
- res.close
250
- return objects
251
- end
252
-
253
- def read_int(res, idx = 0)
254
- val = res.next[idx].to_i
255
- res.close
256
- return val
257
- end
258
-
259
- def get_row(res)
260
- res.next
261
- end
262
-
263
- end
264
-
265
- end
@@ -1,356 +0,0 @@
1
- # * Anastasios Koutoumanos <ak@navel.gr>
2
- # (c) 2004-2005 Navel, all rights reserved.
3
- # $Id: sqlserver.rb 17 2005-04-14 16:03:40Z gmosx $
4
-
5
- begin
6
- require 'dbi'
7
- rescue Object => ex
8
- Logger.error 'Ruby-DBI bindings not present or ADO support not available.'
9
- Logger.error ex
10
- end
11
-
12
- require 'og/adapters/base'
13
-
14
- module Og
15
-
16
- # The Microsoft SQL Server Sql Server adapter. This adapter
17
- # communicates with a Microsoft SQL Server sql server rdbms.
18
- # This adapter will ONLY work on Windows systems, since it
19
- # relies on Win32OLE is apparently only available on Windows.
20
- # It relies on the ADO support in the DBI module. If you are using the
21
- # one-click installer of Ruby, then you already have DBI installed, but
22
- # the ADO module is *NOT* installed. You will need to get the latest
23
- # source distribution of Ruby-DBI from http://ruby-dbi.sourceforge.net/
24
- # unzip it, and copy the file <tt>src/lib/dbd_ado/ADO.rb</tt> to
25
- # <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
26
- # (you will need to create the ADO directory). Once you've installed
27
- # that file, you are ready to go.
28
- # Originally based on the sqlserver_adapter.rb -- the ActiveRecord
29
- # adapter for Microsoft SQL Server by Joey Gibson and DeLynn Berry.
30
- # Information related to the SQL Server datatypes:
31
- # http://msdn.microsoft.com/library/default.asp?url=/library/en-us/architec/8_ar_da_4ucz.asp
32
- # For extra documentation see lib/og/adapter.rb
33
-
34
- class SqlserverAdapter < Adapter
35
-
36
- def initialize
37
- super
38
- @typemap.update(
39
- Integer => 'int', # drak: maybe smallint?
40
- Fixnum => 'int',
41
- Float => 'float',
42
- String => 'varchar(1024)', # drak: maybe nvarchar for unicode
43
- Time => 'datetime', # drak: maybe smalldatime?
44
- Date => 'smalldatetime',
45
- TrueClass => 'bit'
46
- )
47
-
48
- @typecast.update(TrueClass => "#\{:s: ? \'1\' : '0' \}")
49
- end
50
-
51
-
52
- def self.timestamp(time = Time.now)
53
- return nil unless time
54
- return time.strftime("%Y-%m-%d %H:%M:%S")
55
- end
56
-
57
- def self.date(date)
58
- return nil unless date
59
- return "#{date.year}-#{date.month}-#{date.mday}"
60
- end
61
-
62
- def read_prop(p, idx)
63
- if p.klass.ancestors.include?(Integer)
64
- return "res[tuple][#{idx}].to_i"
65
- elsif p.klass.ancestors.include?(Float)
66
- return "res[tuple][#{idx}].to_f"
67
- elsif p.klass.ancestors.include?(String)
68
- return "res[tuple][#{idx}]"
69
- elsif p.klass.ancestors.include?(Time)
70
- return "#{self.class}.parse_timestamp(res[tuple][#{idx}])"
71
- elsif p.klass.ancestors.include?(Date)
72
- return "#{self.class}.parse_date(res[tuple][#{idx}])"
73
- elsif p.klass.ancestors.include?(TrueClass)
74
- return "('0' != res[tuple][#{idx}])"
75
- else
76
- return "YAML::load(res[tuple][#{idx}])"
77
- end
78
- end
79
-
80
- def create_db(database, user = nil, password = nil)
81
- raise 'Not implemented!'
82
-
83
- ### how can we get a valid connection when the database does not already exist?
84
- begin
85
- conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=master;User Id=sa;Password=sa;")
86
- conn["AutoCommit"] = true
87
- conn.execute("CREATE DATABASE #{database}")
88
- conn.commit
89
- Logger.warn "Ignoring credentials for database creation (not implemented)" if user
90
- Logger.info "Created database '#{database}'."
91
- super
92
- rescue Exception => ex
93
- # gmosx: any idea how to better test this?
94
- if ex.to_s =~ /0x80020009/i
95
- Logger.warn "Cannot drop database '#{database}', it does not exist!"
96
- super
97
- else
98
- raise
99
- end
100
- end
101
- end
102
-
103
- def drop_db(database, user = nil, password = nil)
104
- raise 'Not implemented!'
105
-
106
- begin
107
- conn = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=localhost;Initial Catalog=master;User Id=sa;Password=sa;")
108
- conn["AutoCommit"] = true
109
- # FIXME: why no drop?
110
- # conn.execute("DROP DATABASE #{database}")
111
- Logger.warn "Ignoring credentials for database drop (not implemented)" if user
112
- Logger.info "Dropped database '#{database}'."
113
- super
114
- rescue Exception => ex
115
- # gmosx: any idea how to better test this?
116
- if ex.to_s =~ /0x80020009/i
117
- Logger.warn "Cannot drop database '#{database}', it does not exist!"
118
- super
119
- else
120
- raise
121
- end
122
- end
123
- end
124
-
125
- def props_for_insert(klass)
126
- klass.__props.reject { |p| :oid == p.symbol }
127
- end
128
-
129
- def insert_code(klass, db)
130
- props = props_for_insert(klass)
131
- values = props.collect { |p| write_prop(p) }.join(',')
132
-
133
- sql = "INSERT INTO #{klass::DBTABLE} (#{props.collect {|p| p.name}.join(',')}) VALUES (#{values});"
134
-
135
- %{
136
- conn.exec "#{sql}"
137
- conn.commit
138
- res = conn.query("SELECT IDENT_CURRENT('#{klass::DBTABLE}')")
139
- @oid = res[0][0].to_i
140
- #res.finish
141
- }
142
- end
143
-
144
- def new_connection(db)
145
- return SqlserverConnection.new(db)
146
- end
147
-
148
- def calc_field_index(klass, db)
149
- res = db.get_connection.store.execute("SELECT TOP 1 * FROM #{klass::DBTABLE}")
150
- meta = db.managed_classes[klass]
151
- columns = res.column_names
152
-
153
- for idx in (0...columns.size)
154
- meta.field_index[columns[idx]] = idx
155
- end
156
-
157
- ensure
158
- res.finish
159
- end
160
-
161
- def create_table(klass, db)
162
- conn = db.get_connection
163
-
164
- fields = create_fields(klass)
165
-
166
- sql = "CREATE TABLE #{klass::DBTABLE} (#{fields.join(', ')}"
167
-
168
- # Create table constrains.
169
- if klass.__meta and constrains = klass.__meta[:sql_constrain]
170
- sql << ", #{constrains.join(', ')}"
171
- end
172
- sql << ");"
173
-
174
- begin
175
- conn.store.execute(sql)
176
- conn.commit
177
- Logger.info "Created table '#{klass::DBTABLE}'."
178
- rescue => ex
179
- if ex.to_s =~ /80040E14/i
180
- Logger.debug 'Table already exists' if $DBG
181
- else
182
- raise
183
- end
184
- end
185
-
186
- # Create indices.
187
-
188
- if klass.__meta and indices = klass.__meta[:sql_index]
189
- for data in indices
190
- idx, options = *data
191
- idx = idx.to_s
192
- pre_sql, post_sql = options[:pre], options[:post]
193
- idxname = idx.gsub(/ /, "").gsub(/,/, "_").gsub(/\(.*\)/, "")
194
- sql << " CREATE #{pre_sql} INDEX #{klass::DBTABLE}_#{idxname}_idx #{post_sql} ON #{klass::DBTABLE} (#{idx});"
195
- begin
196
- conn.store.execute(sql)
197
- conn.commit
198
- rescue => ex
199
- if ex.to_s =~ /80040E14/i
200
- Logger.debug 'Table already exists' if $DBG
201
- else
202
- raise
203
- end
204
- end
205
- end
206
- end
207
-
208
- # Create join tables if needed. Join tables are used in
209
- # 'many_to_many' relations.
210
-
211
- if klass.__meta and joins = klass.__meta[:sql_join]
212
- for data in joins
213
- # the class to join to and some options.
214
- join_name, join_class, options = *data
215
-
216
- # gmosx: dont use DBTABLE here, perhaps the join class
217
- # is not managed yet.
218
- join_table = "#{self.class.join_table(klass, join_class, join_name)}"
219
- join_src = "#{self.class.encode(klass)}_oid"
220
- join_dst = "#{self.class.encode(join_class)}_oid"
221
- begin
222
- conn.store.execute("CREATE TABLE #{join_table} ( key1 integer NOT NULL, key2 integer NOT NULL )").finish
223
- conn.store.execute("CREATE INDEX #{join_table}_key1_idx ON #{join_table} (key1)").finish
224
- conn.store.execute("CREATE INDEX #{join_table}_key2_idx ON #{join_table} (key2)").finish
225
- conn.commit
226
- rescue => ex
227
- # gmosx: any idea how to better test this?
228
- if ex.to_s =~ /80040E14/i
229
- Logger.debug "Join table already exists" if $DBG
230
- else
231
- raise
232
- end
233
- end
234
- end
235
- end
236
-
237
- ensure
238
- db.put_connection
239
- end
240
-
241
- def drop_table(klass)
242
- super
243
- exec "DROP TABLE #{klass::DBTABLE}"
244
- end
245
-
246
- # Generate the property for oid.
247
-
248
- def eval_og_oid(klass)
249
- klass.class_eval %{
250
- prop_accessor :oid, Fixnum, :sql => 'int NOT NULL IDENTITY(1, 1) PRIMARY KEY'
251
- }
252
- end
253
-
254
- end
255
-
256
- # The SqlserverConnection connection.
257
-
258
- class SqlserverConnection < Connection
259
-
260
- def initialize(db)
261
- super
262
- config = db.config
263
-
264
- begin
265
- @store = DBI.connect("DBI:ADO:Provider=SQLOLEDB;Data Source=#{config[:address]};Initial Catalog=#{config[:database]};User Id=#{config[:user].to_s};Password=#{config[:password].to_s};")
266
- rescue => ex
267
- # gmosx, FIXME: drak, fix this!
268
- if ex.to_s =~ /database .* does not exist/i
269
- Logger.info "Database '#{config[:database]}' not found!"
270
- @db.adapter.create_db(config[:database], config[:user])
271
- retry
272
- end
273
- raise
274
- end
275
- end
276
-
277
- def close
278
- @store.disconnect
279
- super
280
- end
281
-
282
- def prepare(sql)
283
- @store.prepare(sql)
284
- end
285
-
286
- def query(sql)
287
- Logger.debug sql if $DBG
288
- begin
289
- res = @store.select_all(sql)
290
- return res
291
- rescue => ex
292
- handle_db_exception(ex, sql)
293
- end
294
- end
295
-
296
- def exec(sql)
297
- Logger.debug sql if $DBG
298
- begin
299
- @store.execute(sql).finish
300
- rescue => ex
301
- handle_db_exception(ex, sql)
302
- end
303
- end
304
-
305
- def start
306
- # no need to do something.
307
- end
308
-
309
- def commit
310
- @store.commit
311
- end
312
-
313
- def rollback
314
- @store.rollback
315
- end
316
-
317
- def valid_res?(res)
318
- return !(res.nil?)
319
- end
320
-
321
- def read_one(res, klass)
322
- return nil unless valid_res?(res)
323
- return nil unless res
324
-
325
- obj = klass.allocate
326
- obj.og_read(res)
327
-
328
- return obj
329
- end
330
-
331
- def read_all(res, klass)
332
- return [] unless valid_res?(res)
333
- objects = []
334
-
335
- for tuple in (0...res.size)
336
- obj = klass.allocate
337
- obj.og_read(res, tuple)
338
- objects << obj
339
- end
340
-
341
- return objects
342
- end
343
-
344
- def read_int(res, idx = 0)
345
- val = res.next[idx].to_i
346
- res.finish
347
- return val
348
- end
349
-
350
- def get_row(res)
351
- res.next
352
- end
353
-
354
- end
355
-
356
- end